DFU target API on NCS v3.4.1

Dear Nordic team,

I am trying to set up OTA updates by using the DFU target API on an nRF9151 DK with MCUboot and TF-M partitions. (NCS v3.4.1) My goal is to use the immutable MCUboot bootloader and update both the application and the TF-M partition. I've declared

SB_CONFIG_BOOTLOADER_MCUBOOT=y

in the project's "sysbuild.conf" file and have set up a board overlay (PROJECT_DIR/boards/nrf9151dk_nrf9151_ns.overlay) with the following partitions.

/ {
    chosen {
        nordic,pm-ext-flash = &gd25wb256;
    };
};

/delete-node/ &boot_partition;
/delete-node/ &slot0_partition;
/delete-node/ &slot1_partition;
/delete-node/ &tfm_ps_partition;
/delete-node/ &tfm_its_partition;
/delete-node/ &tfm_otp_partition;
/delete-node/ &storage_partition;

&flash0 {
    /*
     * Flash partition layout for non-minimal TF-M in combination with MCUboot
     *
     * 0x0000_0000 BL2 - MCUBoot (64 KB)
     * 0x0001_0000 Primary image area (448 KB):
     *    0x0001_0000 Secure     image primary (192 KB)
     *    0x0004_0000 Non-secure image primary (256 KB)
     * 0x0008_0000 Secondary image area (448 KB):
     *    0x0008_0000 Secure     image secondary (192 KB)
     *    0x000b_0000 Non-secure image secondary (256 KB)
     * 0x000f_0000 Protected Storage Area (16 KB)
     * 0x000f_4000 Internal Trusted Storage Area (8 KB)
     * 0x000f_6000 OTP / NV counters area (8 KB)
     * 0x000f_8000 Non-secure storage, used when built with NRF_NS_STORAGE=ON,
     *             otherwise unused (32 KB)
     */
    partitions {
        ranges;
        #address-cells = <1>;
        #size-cells = <1>;

        boot_partition: partition@0 {
            compatible = "zephyr,mapped-partition";
            label = "mcuboot";
            reg = <0x00000000 0x10000>;
        };

        slot0_partition: partition@10000 {
            compatible = "zephyr,mapped-partition";
            label = "image-0";
            reg = <0x00010000 0x70000>;
            ranges = <0x0 0x10000 0x70000>;
            #address-cells = <1>;
            #size-cells = <1>;

            slot0_s_partition: partition@0 {
                compatible = "zephyr,mapped-partition";
                label = "image-0-secure";
                reg = <0x00000000 0x30000>;
            };

            slot0_ns_partition: partition@30000 {
                compatible = "zephyr,mapped-partition";
                label = "image-0-nonsecure";
                reg = <0x00030000 0x40000>;
            };
        };

        slot1_partition: partition@80000 {
            compatible = "zephyr,mapped-partition";
            label = "image-1";
            reg = <0x00080000 0x70000>;
            ranges = <0x0 0x80000 0x70000>;
            #address-cells = <1>;
            #size-cells = <1>;

            slot1_s_partition: partition@0 {
                compatible = "zephyr,mapped-partition";
                label = "image-1-secure";
                reg = <0x00000000 0x30000>;
            };

            slot1_ns_partition: partition@30000 {
                compatible = "zephyr,mapped-partition";
                label = "image-1-nonsecure";
                reg = <0x00030000 0x40000>;
            };
        };

        tfm_ps_partition: partition@f0000 {
            compatible = "zephyr,mapped-partition";
            label = "tfm-ps";
            reg = <0x000f0000 0x00004000>;
        };

        tfm_its_partition: partition@f4000 {
            compatible = "zephyr,mapped-partition";
            label = "tfm-its";
            reg = <0x000f4000 0x00002000>;
        };

        tfm_otp_partition: partition@f6000 {
            compatible = "zephyr,mapped-partition";
            label = "tfm-otp";
            reg = <0x000f6000 0x00002000>;
        };

        storage_partition: partition@f8000 {
            compatible = "zephyr,mapped-partition";
            label = "storage";
            reg = <0x000f8000 0x00008000>;
        };
    };
};

/delete-node/ &sram0_s;
/delete-node/ &sram0_ns;

&sram0 {
    /*
     * SRAM partition layout for non-minimal TF-M
     *
     * 0x2000_0000  Secure RAM                      (   88 kB)
     * 0x2001_6000  Non-secure RAM area             (  168 kB)
     *   0x2001_6000  Modem shared area             ( 17768 B)
     *     0x2001_6000  control                     (  1256 B)
     *     0x2001_64e8  app -> cell                 (  8320 B)
     *     0x2001_8568  cell -> app                 (    8 kB)
     *   0x2001_a568  Application RAM               (154264 B)
     */
    sram0_s: sram@0 {
        reg = <0x0 0x16000>;
    };

    sram0_ns: sram@16000 {
        reg = <0x16000 0x2a000>;
        #address-cells = <1>;
        #size-cells = <1>;
        ranges = <0x0 0x16000 0x2a000>;

        /* Must be in first 128 kB of RAM. */
        sram0_ns_modem: sram0_ns@0 {
            /* Modem (shared) memory */
            reg = <0x0 0x4568>;
            #address-cells = <1>;
            #size-cells = <1>;
            ranges = <0x0 0x0 0x4568>;

            cpuapp_cpucell_ipc_shm_ctrl: sram0_ns_modem@0 {
                /* Modem IPC control */
                reg = <0x0 0x4e8>;
            };

            cpuapp_cpucell_ipc_shm_heap: sram0_ns_modem@4e8 {
                /* Modem IPC transmit */
                reg = <0x4e8 0x2080>;
            };

            cpucell_cpuapp_ipc_shm_heap: sram0_ns_modem@2568 {
                /* Modem IPC receive */
                reg = <0x2568 0x2000>;
            };
        };

        sram0_ns_app: sram0_ns@4568 {
            /* Non-Secure application memory */
            reg = <0x4568 0x25a98>;
        };
    };
};

&gd25wb256 {
    status = "okay";

    partitions {
        compatible = "fixed-partitions";
        #address-cells = <1>;
        #size-cells = <1>;

        lfs_partition: partition@0 {
            label = "lfs_storage";
            // reg = <0x00000000 0x00040000>; /* 256kB */
            reg = <0x00000000 0x02000000>; /* 32MB */
        };
    };
};

The code initializes the DFU target, downloads update chunks via CoAP and writes the chunks with `dfu_target_write`. The code compiles and runs, but causes a "SECURE FAULT" when running `dfu_target_write`:

[00:00:23.089,080] <err> os: ***** SECURE FAULT *****
[00:00:23.089,080] <err> os:   Address: 0x80000
[00:00:23.089,111] <err> os:   Attribution unit violation
[00:00:23.089,141] <err> os: r0/a1:  0x00080000  r1/a2:  0x00000000  r2/a3:  0xffffffff
[00:00:23.089,172] <err> os: r3/a4:  0x40039000 r12/ip:  0x00000100 r14/lr:  0x0004d47b
[00:00:23.089,202] <err> os:  xpsr:  0x21000000
[00:00:23.089,233] <err> os: Faulting instruction address (r15/pc): 0x000532fe
[00:00:23.089,324] <err> os: >>> ZEPHYR FATAL ERROR 41: Unknown error on CPU 0
[00:00:23.089,416] <err> os: Current thread: 0x200203c8 (main)
[00:00:23.282,043] <err> os: Halting system

My thoughts are that the "SECURE FAULT" might be caused, because the `slot1_partition` might be treated as a secure partition and therefore cannot be written to. (the fault points to the start of slot1_partitions's flash range) Also, I suspect that the image number passed into `dfu_target_init` might be incorrect. At first I though it would have to be set to `0` or `CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER`, but I am not sure about that. My current DFU target initialization looks like this:

#define IMAGE_NUMBER 0

...

int32_t img_type = dfu_target_img_type(chunk, chunk_len);
err = dfu_target_init(img_type, IMAGE_NUMBER, file_size, NULL);
if (err < 0) {
	LOG_DBG("Failed to initialize DFU target, errno: %d", err);
	goto out;
}

...

I've also tried to reference the board overlay in the "PROJECT_DIR/sysbuild_mcubot/boards" directory. There I've added a "nrf9151dk_nrf9151_ns.overlay" file with the following content:

#include "../../../boards/nrf9151dk_nrf9151_ns.overlay"
#include "../app.overlay"

The included "app.overlay" file has:

/ {
	chosen {
		zephyr,code-partition = &boot_partition;
	};
};

Am I missing some definitions in the overlay file, or does Sysbuild have to be made aware of the partitions some other way?

Is the image number `0` correct for this case?

To allow for modem delta updates in the next step, is this setup viable and can the same DFU target procedure and initialization be used?

Guidance is greatly appreciated.

Best,

Tom

  • Hi,

    With normal MCUboot swap behaviour, MCuboot does not know about TF-M. It just swaps the application as one blob.

    The application uploads the DFU candidate to the secondary slot. And the application only has Non-Secure access.
    So the secondary slot needs to be fully Non-Secure.

    From your DTS file:

    "
         * 0x0008_0000 Secondary image area (448 KB):
         *    0x0008_0000 Secure     image secondary (192 KB)
         *    0x000b_0000 Non-secure image secondary (256 KB)
    "

    For a normal swap-based DFU, I would recommend not splitting the Secondary slot int two partitions; Just use one.

    Regards,
    Sigurd Hellesvik

  • Hey Sigurd,

    thank you for the quick reply!

    So, you would update the slot1_partition as the following?

    slot1_partition: partition@80000 {
        compatible = "zephyr,mapped-partition";
        label = "image-1";
        reg = <0x00080000 0x70000>;
        ranges = <0x0 0x80000 0x70000>;
        #address-cells = <1>;
        #size-cells = <1>;
    };

    Do I need to add something like this?

    / {
        chosen {
            nordic,slot1_partition = &slot1_partition;
        };
    };

    I tried combinations of the above, but I still get the same secure fault. 

    May I ask you to explain which image number is required when using "dfu_target_init"?

    Best,

    Tom

  • > My goal is to use the immutable MCUboot bootloader

    Do you mean a two stage approach? Because 

    > boot_partition: partition@0

    indicates, that only one is used.

    > The code compiles and runs, but causes a "SECURE FAULT" when running `dfu_target_write`:

    Yes, that's the case since start of the year on different devices and NCS versions. It was caused by different issues, mainly failing to select the right partition to upload the update in "flash_img.c". Maybe it works, if you really switch to the "immutable 2 stage" approach, because that seems to be the used one in the samples.

    For the single stage bootloader, the current issue is NCS 3.4.0, nRF91, DFU without external flash and without SECURE_BOOT is broken . You may cherry-pick the patch from fix.

  • Hey Achim,

    I saw that thread, but did not fully recognize it as being relevant here...

    Do you mean a two stage approach?

    My understanding is probably lack-luster:

    I meant a single stage approach. The documentation for the secure bootloader chain (nrfconnectdocs.nordicsemi.com/.../bootloader.html states that "The first implementation (i.e. the ) provides the first stage in the chain, the immutable nRF Secure Immutable Bootloader, which could be either nRF Secure Immutable Bootloader or MCUboot. It does not support bootloader upgradability, but it is useful if you need just the capability to update your application." 

    Further down in the same documentation (https://nrfconnectdocs.nordicsemi.com/ncs/latest/nrf/app_dev/bootloaders_dfu/mcuboot_nsib/bootloader.html#immutable-bootloader) the following is stated: "You should add a second-stage bootloader only when necessary by the design or firmware upgrade needs. Adding the second stage bootloader for no reason will lead to a degradation of the system’s overall security, as attackers can exploit bugs that may exist in either bootloader.".

    In this documentation (https://nrfconnectdocs.nordicsemi.com/ncs/latest/nrf/app_dev/bootloaders_dfu/mcuboot_nsib/bootloader_quick_start.html#supported-features-and-configurations) it is listed that NSIB only supports Dual-slot direct-xip upgrades.

    That would conflict with the secure-labeled TF-M partitions again, wouldn't it?

    Therefore my approach was to go with a single stage approach using only MCUboot with image swapping.

    Do you or  have a different take on the documentation's recommendation to not use a two stage approach if it is not strictly necessary?

    Or is my understanding false, that Dual-slot direct-xip upgrades would conflict with secure partitions because the non-secure partition would have to write to a secure flash section? 

    Thanks,

    Tom 

  • Just to mention: I'm a developer and not related to Nordic ;-).

    There are different boot-loader setups, and one has the name "SECURE_BOOT" and uses an immutable b0 boot-loader and additional mcuboot (2 stages). So the terms you used truggered me to clarify, what your intention is.

    Your app crashes, because in your partition setup "flash_img.c" gets it wrong. It takes then the primary slot for upload and that crashes.

    At the begin of the year, it also toke the primary slot for the Thingy:91/X, but there the root cause was slightly different.

    The patch is pretty small, you may even try to edit "flash_img.c" to see, if that works for you.

    >  it is listed that NSIB only supports Dual-slot direct-xip upgrades.

    Not sure, what they mean by "image swap", at least I'm mainly common with the 

    "Dual-slot direct-xip"

    but that swaps the slots as well, means, download to secondary slot, and on update swap primary and secondary. For that I write the app (tf-m + ns-app) to the secondary slot, and mucboot copies both to the primary and executes it from there.

    Anyway, let me recommend, you start with the fix and we will see, if that is changing something.

Related