tfm_ps and the mcuboot trailer end up in the same partition.

Background:

Over the past few weeks, I have been working on enabling TLS1.2 for an nRF9160 with Ethernet (W5500). My primary objective is to have both Ethernet and LTE-M functioning within a single image, although this is not the main focus of this ticket. To communicate with the modem, my image needs to be built in as non-secure (ns). However, when using mbedTLS with entropy, the CC3XX hardware is used, which is only accessible through secure (s) partition, necessitating the use of TF-M. Enabling the required configurations for TLS also activates Internal Trusted Storage (ITS) and Protected Storage (PS). While I haven't yet investigated why ITS and PS are enabled, I assume it is for storing CA certificates.

For reference, I have included the configurations I added for Ethernet. The Ethernet connection appears to be functioning correctly, and I can connect using multiple different backends.

# ===== Ethernet =====
# W5500
CONFIG_SPI=y
CONFIG_ETH_DRIVER=y
CONFIG_ETH_W5500=y
CONFIG_ETH_W5500_TIMEOUT=1000

CONFIG_NETWORKING=y
CONFIG_NET_NATIVE=y
CONFIG_NET_RX_STACK_SIZE=2048
CONFIG_NET_SOCKETS_OFFLOAD=n
CONFIG_NET_TCP_WORKQ_STACK_SIZE=2048
CONFIG_NET_SOCKETS=y
CONFIG_TLS_MAX_CREDENTIALS_NUMBER=6
CONFIG_NET_SOCKETS_TLS_MAX_CREDENTIALS=6
CONFIG_NET_SOCKETS_TLS_MAX_CONTEXTS=1
CONFIG_NET_SOCKETS_TLS_MAX_CLIENT_SESSION_COUNT=1
CONFIG_NET_SOCKETS_SOCKOPT_TLS=y
CONFIG_NET_SOCKETS_POLL_MAX=6
CONFIG_POSIX_API=y
CONFIG_NET_IPV4=y
CONFIG_NET_IPV6=y
CONFIG_NET_TCP=y
CONFIG_NET_UDP=y
CONFIG_NET_DHCPV4=y
CONFIG_NET_DHCPV4_OPTION_CALLBACKS=y
CONFIG_NET_CONTEXT_SNDTIMEO=y
CONFIG_NET_CONTEXT_RCVTIMEO=y
CONFIG_NET_MGMT_EVENT_STACK_SIZE=2048
CONFIG_NET_LOG=y
CONFIG_NET_TCP_LOG_LEVEL_OFF=y
CONFIG_SNTP=y
CONFIG_NET_MGMT=y
CONFIG_NET_MGMT_EVENT=y
CONFIG_NET_L2_ETHERNET=y
CONFIG_NET_L2_ETHERNET_MGMT=y
CONFIG_NET_CONNECTION_MANAGER=y

# Network buffers
CONFIG_NET_PKT_RX_COUNT=32
CONFIG_NET_PKT_TX_COUNT=16
CONFIG_NET_BUF_RX_COUNT=64
CONFIG_NET_BUF_TX_COUNT=32

# DNS
CONFIG_DNS_RESOLVER=y
CONFIG_DNS_RESOLVER_CACHE=y
CONFIG_DNS_SERVER_IP_ADDRESSES=y
CONFIG_DNS_SERVER1="8.8.8.8"
CONFIG_DNS_SERVER2="1.1.1.1"
CONFIG_NET_SOCKETS_DNS_TIMEOUT=5000
CONFIG_MDNS_RESOLVER=y
CONFIG_DNS_RESOLVER_AI_MAX_ENTRIES=4

# MQTT
CONFIG_MQTT_LIB=y
CONFIG_MQTT_LIB_TLS=y
CONFIG_MQTT_KEEPALIVE=60
CONFIG_MQTT_CLEAN_SESSION=y
CONFIG_MQTT_LOG_LEVEL_OFF=y

# TFM
CONFIG_BUILD_WITH_TFM=y
CONFIG_TFM_PROFILE_TYPE_NOT_SET=y
CONFIG_PM_PARTITION_SIZE_TFM_SRAM=0xe000
CONFIG_PM_PARTITION_SIZE_TFM=0x1fe00

# PSA
CONFIG_NRF_SECURITY=y
CONFIG_MBEDTLS_LIBRARY_NRF_SECURITY=y

# MBEDTLS
CONFIG_MBEDTLS=y
CONFIG_MBEDTLS_LEGACY_CRYPTO_C=y
CONFIG_MBEDTLS_ENABLE_HEAP=y
CONFIG_MBEDTLS_HEAP_SIZE=37000
CONFIG_MBEDTLS_TLS_VERSION_1_2=y
CONFIG_MBEDTLS_PSA_CRYPTO_C=y
CONFIG_MBEDTLS_ENTROPY_C=y
CONFIG_OBERON_BACKEND=n
CONFIG_MBEDTLS_ECP_C=y
CONFIG_MBEDTLS_ECDSA_C=y
CONFIG_MBEDTLS_RSA_C=y
CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_ENABLED=y
CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED=y
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED=y
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED=y
CONFIG_MBEDTLS_PK_C=y
CONFIG_MBEDTLS_ECDH_C=y
CONFIG_MBEDTLS_DHM_C=y
CONFIG_MBEDTLS_CTR_DRBG_C=y
CONFIG_MBEDTLS_PK_PARSE_C=y
CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=2048
CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=1024
CONFIG_MBEDTLS_SSL_SERVER_NAME_INDICATION=y
CONFIG_MBEDTLS_SSL_RENEGOTIATION=y
CONFIG_MBEDTLS_PEM_PARSE_C=y
#CONFIG_MBEDTLS_PEM_WRITE_C=y
CONFIG_MBEDTLS_MPI_MAX_SIZE=512
CONFIG_MBEDTLS_SHA256_C=y
CONFIG_MBEDTLS_SHA384_C=y
CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y
CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y
CONFIG_MBEDTLS_AES_FEWER_TABLES=y
CONFIG_MBEDTLS_AES_ROM_TABLES=y

With TLS now operational, I began reintroducing parts of my project but encountered issues getting MCUBoot to work with TF-M PS. Specifically, my code crashes when calling boot_is_img_confirmed(). This function reads the "Image OK" section of the MCUBoot trailer. During debugging, I noticed that the trailer is located in the secure part of the code, specifically within the TF-M PS partition. I don't think this is intented.

To prevent TF-M PS and the MCUBoot trailer from writing to the same partition, I relocated the TF-M ITS and TF-M PS partitions so that they are positioned before the app. This ensures that the MCUBoot trailer is placed after the app.

mcuboot:
  address: 0x00000
  end_address: 0x10000
  region: flash_primary
  size: 0x10000
mcuboot_primary:
  address: 0x10000
  end_address: 0xf7000
  orig_span: &id001
  - mcuboot_pad
  - tfm
  - app
  region: flash_primary
  size: 0xE0000
  span: *id001
tfm_secure:
  address: 0x10000
  end_address: 0x30000
  size: 0x20000
  span: [mcuboot_pad, tfm]
mcuboot_pad:
  address: 0x10000
  region: flash_primary
  placement:
    align:
      start: 0x8000
    before:
    - mcuboot_primary_app
  size: 0x200
tfm:
  address: 0x10200
  end_address: 0x30000
  size: 0x1fe00
tfm_storage:
  address: 0x30000
  end_address: 0x38000
  orig_span: &id008
  - tfm_ps
  - tfm_its
  - tfm_otp_nv_counters
  region: flash_primary
  size: 0x8000
  span: *id008
tfm_its:
  address: 0x30000
  end_address: 0x32000
  placement:
    align:
      start: 0x8000
    before:
    - tfm_otp_nv_counters
  region: flash_primary
  size: 0x2000
tfm_otp_nv_counters:
  address: 0x32000
  end_address: 0x34000
  placement:
    align:
      start: 0x8000
    before:
    - tfm_ps
  region: flash_primary
  size: 0x2000
tfm_ps:
  address: 0x34000
  end_address: 0x38000
  placement:
    align:
      start: 0x8000
    before:
    - end
  region: flash_primary
  size: 0x4000
tfm_nonsecure:
  address: 0x38000
  end_address: 0xf7000
  size: 0xbf000
  span: [app]
app:
  address: 0x38000
  end_address: 0xf7000
  region: flash_primary
  size: 0xbf000
config_factory:
  address: 0x00f7000
  end_address: 0xf8000
  region: flash_primary
  size: 0x00001000
storage_partition:
  address: 0x000f8000
  end_address: 0x100000
  region: flash_primary
  size: 0x00008000
mcuboot_secondary:
  address: 0x00000000
  device: DT_CHOSEN(nordic_pm_ext_flash)
  end_address: 0xdf000
  region: external_flash
  size: 0xdf000
config_primary:
  address: 0xdf000
  device: DT_CHOSEN(nordic_pm_ext_flash)
  end_address: 0xe0000
  region: external_flash
  size: 0x01000
config_secondary:
  address: 0xe0000
  device: DT_CHOSEN(nordic_pm_ext_flash)
  end_address: 0xe1000
  region: external_flash
  size: 0x01000

This change resolved the crash on boot_is_img_confirmed(). However, after a software or hardware reset, the system fails to boot again. I suspect this is because MCUBoot checks a CRC before booting an image. On the first boot, the CRC is correct, but once TF-M PS writes to the flash, the CRC becomes invalid.

Environment:

Hardware: nrf9160 with w5500 on a custom board
Modem version: 1.3.7
nrf sdk versions: 3.0.2
zephyr version: 4.0.99-ncs1
zephyr toolchain version: 0.17.2



Questions:

At this point, I am unsure how to proceed and have several questions to confirm my assumptions:

  1. What are TF-M ITS and TF-M PS used for when working with TLS?
  2. Is it intentional that the MCUBoot trailer is saved inside TF-M PS?
  3. Does TF-M PS need to be located outside mcuboot_primary so that MCUBoot can perform a CRC check over this partition?
  4. Why isn't the MCUBoot trailer included in the generated partitions.yml?
  5. Are there additional configurations needed to get both MCUBoot and TF-M PS to work?


Additional Question:

I have a more general question regarding TF-M. The TF-M part seems quite experimental to me. I see MCUBoot as one of the primary features of Zephyr. Any setting that could potentially break this feels like an experimental feature. The compiler warnings, such as "warning: Experimental symbol TFM_EXPERIMENTAL is enabled," seem to confirm my expectations. Why does it feel almost necessary to use TF-M for the nRF9160 when it is still an experimental feature of Zephyr?

The warning can of course also indicate that I still have a specific experimental configuration so it is definitely possible that I'm mistaken in this view.
  • Hi Ben,

    I need to look a bit further into this, but here is my initial feedback:

    What are TF-M ITS and TF-M PS used for when working with TLS?

    TLS certificates can be stored in TF-M Protected Storage. You can see an example of TLS with TF-M here.

    Is it intentional that the MCUBoot trailer is saved inside TF-M PS?

    No, the MCUboot image trailer is part of the image and need to fit in within the mcuboot slot. This cannot overlay with any other region/partition in the memory layout/map.

    Does TF-M PS need to be located outside mcuboot_primary so that MCUBoot can perform a CRC check over this partition?

    Yes, it must be located outside. The mcuboot_primary slot is only for holding the application image (binary that never changes except during DFU).

    Why isn't the MCUBoot trailer included in the generated partitions.yml?

    It is part of the image/application binary, and does not need a separate partition.

    There should not be, but you need to make sure the memory layout is sensible with no conflicts.

    Why does it feel almost necessary to use TF-M for the nRF9160 when it is still an experimental feature of Zephyr?

    The TF-M delievered as part of the nRF Connect SDK is not experimental, and is requiered for most if not all nRF91 applications (as the modem is only accessibel from non-secure). The minimal configureation is supported, but other configurations are experimental (see here).

    Br,

    Einar

  • Hi Einar,

    Thank you for your quick response, it helped clarify several aspects for me.

    No, the MCUboot image trailer is part of the image and need to fit in within the mcuboot slot. This cannot overlay with any other region/partition in the memory layout/map.

    To verify your explanation, I repeated my debugging test. My findings confirm that the MCUBoot trailer is located inside the MCUBoot primary partition.

    For this test, I reverted the changes made to pm_static.yml.

    mcuboot:
      address: 0x00000
      end_address: 0x10000
      region: flash_primary
      size: 0x10000
    mcuboot_pad:
      address: 0x10000
      region: flash_primary
      placement:
        align:
          start: 0x8000
        before:
        - mcuboot_primary_app
      size: 0x200
    mcuboot_primary:
      address: 0x10000
      end_address: 0xf0000
      orig_span: &id001
      - mcuboot_pad
      - tfm
      - app
      region: flash_primary
      size: 0xE0000
      span: *id001
    app:
      address: 0x18000
      end_address: 0xf7000
      region: flash_primary
      size: 0xdf000
    config_factory:
      address: 0x00f7000
      end_address: 0xf8000
      region: flash_primary
      size: 0x00001000
    storage_partition:
      address: 0x000f8000
      end_address: 0x100000
      region: flash_primary
      size: 0x00008000
    mcuboot_secondary:
      address: 0x00000000
      device: DT_CHOSEN(nordic_pm_ext_flash)
      end_address: 0xdf000
      region: external_flash
      size: 0xdf000
    config_primary:
      address: 0xdf000
      device: DT_CHOSEN(nordic_pm_ext_flash)
      end_address: 0xe0000
      region: external_flash
      size: 0x01000
    config_secondary:
      address: 0xe0000
      device: DT_CHOSEN(nordic_pm_ext_flash)
      end_address: 0xe1000
      region: external_flash
      size: 0x01000


    Zephyr subsequently generated the following partitions.yml.
    EMPTY_0:
      address: 0xf4000
      end_address: 0xf7000
      placement:
        after:
        - tfm_ps
      region: flash_primary
      size: 0x3000
    EMPTY_1:
      address: 0xea000
      end_address: 0xf0000
      placement:
        after:
        - tfm_otp_nv_counters
      region: flash_primary
      size: 0x6000
    EMPTY_2:
      address: 0xe2000
      end_address: 0xe8000
      placement:
        after:
        - tfm_its
      region: flash_primary
      size: 0x6000
    EMPTY_3:
      address: 0xde000
      end_address: 0xe0000
      placement:
        after:
        - nvs_storage
      region: flash_primary
      size: 0x2000
    app:
      address: 0x30000
      end_address: 0xd8000
      region: flash_primary
      size: 0xa8000
    config_factory:
      address: 0xf7000
      end_address: 0xf8000
      region: flash_primary
      size: 0x1000
    config_primary:
      address: 0xdf000
      device: DT_CHOSEN(nordic_pm_ext_flash)
      end_address: 0xe0000
      region: external_flash
      size: 0x1000
    config_secondary:
      address: 0xe0000
      device: DT_CHOSEN(nordic_pm_ext_flash)
      end_address: 0xe1000
      region: external_flash
      size: 0x1000
    external_flash:
      address: 0xe1000
      end_address: 0x800000
      region: external_flash
      size: 0x71f000
    mcuboot:
      address: 0x0
      end_address: 0x10000
      region: flash_primary
      size: 0x10000
    mcuboot_pad:
      address: 0x10000
      end_address: 0x10200
      placement:
        align:
          start: 0x8000
        before:
        - mcuboot_primary_app
      region: flash_primary
      size: 0x200
    mcuboot_primary:
      address: 0x10000
      end_address: 0xf0000
      orig_span: &id001
      - mcuboot_pad
      - tfm
      - app
      region: flash_primary
      size: 0xe0000
      span: *id001
    mcuboot_primary_app:
      address: 0x10200
      end_address: 0xd8000
      orig_span: &id002
      - app
      - tfm
      region: flash_primary
      size: 0xc7e00
      span: *id002
    mcuboot_secondary:
      address: 0x0
      device: DT_CHOSEN(nordic_pm_ext_flash)
      end_address: 0xdf000
      region: external_flash
      size: 0xdf000
    mcuboot_sram:
      address: 0x20000000
      end_address: 0x2000e000
      orig_span: &id003
      - tfm_sram
      region: sram_primary
      size: 0xe000
      span: *id003
    nonsecure_storage:
      address: 0xd8000
      end_address: 0xde000
      orig_span: &id004
      - nvs_storage
      region: flash_primary
      size: 0x6000
      span: *id004
    nrf_modem_lib_ctrl:
      address: 0x2000e000
      end_address: 0x2000e4e8
      inside:
      - sram_nonsecure
      placement:
        after:
        - tfm_sram
        - start
      region: sram_primary
      size: 0x4e8
    nrf_modem_lib_rx:
      address: 0x20010568
      end_address: 0x20012568
      inside:
      - sram_nonsecure
      placement:
        after:
        - nrf_modem_lib_tx
      region: sram_primary
      size: 0x2000
    nrf_modem_lib_sram:
      address: 0x2000e000
      end_address: 0x20012568
      orig_span: &id005
      - nrf_modem_lib_ctrl
      - nrf_modem_lib_tx
      - nrf_modem_lib_rx
      region: sram_primary
      size: 0x4568
      span: *id005
    nrf_modem_lib_tx:
      address: 0x2000e4e8
      end_address: 0x20010568
      inside:
      - sram_nonsecure
      placement:
        after:
        - nrf_modem_lib_ctrl
      region: sram_primary
      size: 0x2080
    nvs_storage:
      address: 0xd8000
      end_address: 0xde000
      inside:
      - nonsecure_storage
      placement:
        align:
          start: 0x8000
        before:
        - tfm_storage
        - end
      region: flash_primary
      size: 0x6000
    otp:
      address: 0xff8108
      end_address: 0xff83fc
      region: otp
      size: 0x2f4
    sram_nonsecure:
      address: 0x2000e000
      end_address: 0x20040000
      orig_span: &id006
      - sram_primary
      - nrf_modem_lib_ctrl
      - nrf_modem_lib_tx
      - nrf_modem_lib_rx
      region: sram_primary
      size: 0x32000
      span: *id006
    sram_primary:
      address: 0x20012568
      end_address: 0x20040000
      region: sram_primary
      size: 0x2da98
    sram_secure:
      address: 0x20000000
      end_address: 0x2000e000
      orig_span: &id007
      - tfm_sram
      region: sram_primary
      size: 0xe000
      span: *id007
    storage_partition:
      address: 0xf8000
      end_address: 0x100000
      region: flash_primary
      size: 0x8000
    tfm:
      address: 0x10200
      end_address: 0x30000
      inside:
      - mcuboot_primary_app
      placement:
        before:
        - app
      region: flash_primary
      size: 0x1fe00
    tfm_its:
      address: 0xe0000
      end_address: 0xe2000
      inside:
      - tfm_storage
      placement:
        align:
          start: 0x8000
        before:
        - tfm_otp_nv_counters
      region: flash_primary
      size: 0x2000
    tfm_nonsecure:
      address: 0x30000
      end_address: 0xd8000
      orig_span: &id008
      - app
      region: flash_primary
      size: 0xa8000
      span: *id008
    tfm_otp_nv_counters:
      address: 0xe8000
      end_address: 0xea000
      inside:
      - tfm_storage
      placement:
        align:
          start: 0x8000
        before:
        - tfm_ps
      region: flash_primary
      size: 0x2000
    tfm_ps:
      address: 0xf0000
      end_address: 0xf4000
      inside:
      - tfm_storage
      placement:
        align:
          start: 0x8000
        before:
        - end
      region: flash_primary
      size: 0x4000
    tfm_secure:
      address: 0x10200
      end_address: 0x30000
      orig_span: &id009
      - tfm
      region: flash_primary
      size: 0x1fe00
      span: *id009
    tfm_sram:
      address: 0x20000000
      end_address: 0x2000e000
      inside:
      - sram_secure
      placement:
        after:
        - start
      region: sram_primary
      size: 0xe000
    tfm_storage:
      address: 0xe0000
      end_address: 0xf4000
      orig_span: &id010
      - tfm_ps
      - tfm_its
      - tfm_otp_nv_counters
      region: flash_primary
      size: 0x14000
      span: *id010
    


    I then focused on debugging the boot_is_img_confirmed() function, where the problem arises.
    rc = flash_area_read(fap, off, magic, BOOT_MAGIC_SZ);

    The offset of this area read is fetched from:
    off = boot_magic_off(fap);

    Just before the crash, the img_confirmed function calls a memcpy() operation, trying to read from address 0xEFFF0.

    My interpretation is that the MCUBoot trailer resides within the mcuboot_primary partition but outside the app partition. In this particular build, this should not pose an issue since the trailer is placed in the EMPTY_1 partition. While my preference would be to have a dedicated partition for the MCUBoot trailer, I understand that in most cases, the EMPTY slots prevent the trailer from being written to unintended locations.

    For now I've located the TFM storage outside the mcuboot_primary partition, and made a mcuboot trailer partion at the end of the mcuboot_primary partition. which seems to resolve the problem.

    One question remains: Why are the tfm_storage (and similarly, the nonsecure_storage) partitions configured within the mcuboot_primary partition by default? Given that the mcuboot_primary partition cannot be modified except during a DFU, what is the rationale for placing storage partitions within it? Additionally, should I also move the nonsecure_storage partition outside the mcuboot_primary partition?

  • Hi,

    I am glad y ou made progress. As far as I could find we have not seen a generic issue with tfm_storage and nonsecure_storage being located within mcuboot_primary by the partition manager before. It normally makes sense to place as little as possible statically, and use dynamic partitioning for evertying else. (Then subsequently take the dynicamically allocated partitions and use as static partitions for subsequent builds, as the partition layout cannot change over time when doing DFU).

  • Heey Einar,
    I'm sorry for my late response, something urgent in the office needed my attention.
    Thanks for the advice you've given thus far, it helped me find my problem.


    As explained in my last ticket is it not possible to place as little as possible in static memory, due to incorrectly placed storage partitions. Fortunately, the solution turned out to be straightforward once I fully understood the underlying issue.

    Hoppefully you're right, and this is an issolated issue.

Related