Custom dual bank bootloader

Hi everyone,

I'm quite new to the Nrf Connect SDK and Zephyr.

I'm trying to implement a simple DFU (over UART) bootloader. The idea is that when the microcontroller powers on, the bootloader selects the area to boot from and checks if a DFU is required.

Since it's a very simple DFU protocol, I'm only sending a binary through UART from another microcontroller on the board (no need for RSA checking).

Using the Device Tree, I've set up two banks. I'm performing the OTA update in the secondary slot at 0x32000 (200 KB long).
Currently, because it's just a proof of concept (POC), the bootloader app is running in the slot0 partition (this will be changed in the future).

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

		slot0_partition: partition@0 {
			label = "primary";
			reg = <0x00000000 0x00032000>; // 200 KB , where the DFU bootloader code is (main.c actually)
		};

		slot1_partition: partition@32000 {
			label = "secondary";
			reg = <0x00032000 0x00032000>; // 200 KB , where the application code will be stored at the moment
		};
	};
};

At the moment, the code is very simple. It successfully erases the secondary partition and then waits to receive the binary over UART. I can successfully write the binary entirely.
However, I can't boot from it. The NRF seems to crash.I used the bl_boot() function from the b0 bootloader as a reference. I correctly set the MSP and VTOR and call the reset handler from the new binary, but nothing seems to work:

void jump_to_image(uint32_t base_address) // here the address of the secondary slot 0x32000
{

        printk("Jumping to firmware at address: 0x%x\n", base_address);

        const struct device *uart_dev = DEVICE_DT_GET(UART);
        if (device_is_ready(uart_dev))
        {
                uart_irq_rx_disable(uart_dev);
                uart_irq_tx_disable(uart_dev);
        }
        // disable all interrupts
        __ISB();
        __disable_irq();

        NVIC_Type *nvic = NVIC;

        // desactivate all interrupts
        for (uint8_t i = 0; i < ARRAY_SIZE(nvic->ICER); i++)
        {
                nvic->ICER[i] = 0xFFFFFFFF;
        }

        // Erase all pending interrupts
        for (uint8_t i = 0; i < ARRAY_SIZE(nvic->ICPR); i++)
        {
                nvic->ICPR[i] = 0xFFFFFFFF;
        }

        // Désactiver le SysTick
        SysTick->CTRL = 0;

        /* Disable fault handlers used by the bootloader */
        SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk;

        // Réinitialiser les handlers de faute (si nécessaire)
#ifndef CONFIG_CPU_CORTEX_M0
        SCB->SHCSR &= ~(SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk |
                        SCB_SHCSR_MEMFAULTENA_Msk);
#endif

        // Activer la MSP si PSP est utilisé
        if (CONTROL_SPSEL_Msk & __get_CONTROL())
        {
                __set_CONTROL(__get_CONTROL() & ~CONTROL_SPSEL_Msk);
        }

        __DSB();
        __ISB();

        // new vector table in base_address
        SCB->VTOR = base_address;

        uint32_t *vector_table = (uint32_t *)base_address;

#if defined(CONFIG_BUILTIN_STACK_GUARD) && defined(CONFIG_CPU_CORTEX_M_HAS_SPLIM)
        /* Reset limit registers to avoid inflicting stack overflow on image
         * being booted.
         */
        __set_PSPLIM(0);
        __set_MSPLIM(0);
#endif

        printk("Calling reset handler at address: 0x%x\n", vector_table[1]);

        // main stack pointer (MSP)
        uint32_t msp = vector_table[0];
        __set_MSP(msp);
        __set_PSP(0);

        // jump to reset handler,
        ((void (*)(void))vector_table[1])();

        // Ce point ne doit jamais être atteint
        CODE_UNREACHABLE;
}


In the debugger it's hard to know what's going on but I've managed to see that I'm getting a BUS ERROR

Can you tell me what I'm missing? It really looks like the bl_boot() function from b0.
And I'm not using MCUBOOT or any other utilities from it

Thanks!!!



Related