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

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

    Remove that, though the "partition manager" is anyway deprecated.

    Funnily enough, after removing this section completely, dfu_target_write() now does not cause a secure fault! The downloaded update chunks can now be written to flash.

  • After I removed this

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

    from the DTSI file, the secure fault did not trigger anymore on dfu_target_write(). The update now completes with this setup:

    /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>;
            };
        };
    };

    Out of interest, I tried your suggestion of removing the sub-partitions from slot1_partition, but that causes a build error.

  • Tom01 said:
    I tried your suggestion of removing the sub-partitions from slot1_partition, but that causes a build error.

    Yeh, I see that they are also separated in the DK parititoning. I will look ionto this.

    Tom01 said:
    from the DTSI file, the secure fault did not trigger anymore on dfu_target_write(). The update now completes with this setup:

    Good to see that you found the fix! Thanks a lot for the help on this Achim!

    I will test if this "nordic,pm-ext-flash" is handled by the SDK with the Partition Manager disabled. And if it still is, I will report this as a bug internally so we can get it fixed.

    Tom01 said:
    My impression from reading the docs is that in "Dual-slot direct-xip" mode, bootloader may start the application from both partitions depending on where the newer version is placed

    Yes. 
    Pro: Faster boot time.
    Con: You need to track which slot is running, cause you will have different DFU images.

    In general, i find that its better to stick with the swap functionality unless you have very hard requirements for boot time.

    Tom01 said:
    Do you use the PSA secure storage by any chance? This should persist if swap mode is used, but I'm not sure if there is a mechanism to persist it when Dual-slot direct-xip is being used.

    DFU strategy (swap vs direct-xip) should not impact storage. DFU only updates application slots, and not storage.

  • > My impression from reading the docs is that in "Dual-slot direct-xip" mode, bootloader may start the application from both partitions depending on where the newer version is placed.

    That would require to generate "relocatable code", but I don't think so.

    Also the "flash_img.c" must then write the updates to different slots depending on which the app is currently running. I don't see that.

    Edited: I found the reference to "SB_CONFIG_SECURE_BOOT_BUILD_S1_VARIANT_IMAGE" ...

    That will make things not that easy. And when the upload-code is on the external flash it must be copied.

    Therefore I guess, that's a misunderstanding. 

  • Do you use the PSA secure storage by any chance? This should persist if swap mode is used, but I'm not sure if there is a mechanism to persist it when Dual-slot direct-xip is being used.

    The secure storage is separated from the running/upload partitions. I don't see any dependency.

Related