Custom update applicaiton

Hi nordic,

I  have successfully completed the Bluetooth over-the-air upgrade using the secure bootloader example. However, we need to add an additional upgrade method. In the application, I have downloaded the new application file along with its corresponding settings file to external flash. How can I modify the secure bootloader so that after the device resets, the new application replaces the old one?

I tried simulating SPI in the bootloader to load the data from the SPI flash into the application region, but I encountered errors when writing the settings file.

SDK 17.1.0  nRF52833

Main code:

/**
 * Copyright (c) 2016 - 2021, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
/** @file
 *
 * @defgroup bootloader_secure_ble main.c
 * @{
 * @ingroup dfu_bootloader_api
 * @brief Bootloader project main file for secure DFU.
 *
 */

#include <stdint.h>
#include "boards.h"
#include "nrf_mbr.h"
#include "nrf_bootloader.h"
#include "nrf_bootloader_app_start.h"
#include "nrf_bootloader_dfu_timers.h"
#include "nrf_dfu.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include "app_error.h"
#include "app_error_weak.h"
#include "nrf_bootloader_info.h"
#include "nrf_delay.h"

#include "m_fstorage.h"
#include "w25qxx.h"

static void on_error(void)
{
    NRF_LOG_FINAL_FLUSH();

#if NRF_MODULE_ENABLED(NRF_LOG_BACKEND_RTT)
    // To allow the buffer to be flushed by the host.
    nrf_delay_ms(100);
#endif
#ifdef NRF_DFU_DEBUG_VERSION
    NRF_BREAKPOINT_COND;
#endif
    NVIC_SystemReset();
}

void app_error_handler(uint32_t error_code, uint32_t line_num, const uint8_t * p_file_name)
{
    NRF_LOG_ERROR("%s:%d", p_file_name, line_num);
    on_error();
}


void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
{
    NRF_LOG_ERROR("Received a fault! id: 0x%08x, pc: 0x%08x, info: 0x%08x", id, pc, info);
    on_error();
}


void app_error_handler_bare(uint32_t error_code)
{
    NRF_LOG_ERROR("Received an error: 0x%08x!", error_code);
    on_error();
}

/**
 * @brief Function notifies certain events in DFU process.
 */
static void dfu_observer(nrf_dfu_evt_type_t evt_type)
{
    switch (evt_type)
    {
        case NRF_DFU_EVT_DFU_FAILED:
        case NRF_DFU_EVT_DFU_ABORTED:
        case NRF_DFU_EVT_DFU_INITIALIZED:
//            bsp_board_init(BSP_INIT_LEDS);
//            bsp_board_led_on(BSP_BOARD_LED_0);
//            bsp_board_led_on(BSP_BOARD_LED_1);
//            bsp_board_led_off(BSP_BOARD_LED_2);
            break;
        case NRF_DFU_EVT_TRANSPORT_ACTIVATED:
//            bsp_board_led_off(BSP_BOARD_LED_1);
//            bsp_board_led_on(BSP_BOARD_LED_2);
            break;
        case NRF_DFU_EVT_DFU_STARTED:
            break;
        default:
            break;
    }
}

/**
 * @brief Function update application data.
 */
//uint8_t  fs_user_data[FS_BUFF_SIZE] = {0};

static void application_update(void)
{
		uint32_t 			 FileLengh;
		uint8_t  			 FileConfig[36];
		uint16_t       FileLenHigh;
		uint16_t       FileLenLow;
		char           CloudMd5[33];
		uint8_t 			 fs_user_data[4];
	
		W25QXX_Init();
		if((W25QXX_Check() == true) || (W25QXX_valid_check() == true))
		{
				W25QXX_WAKEUP();
				W25QXX_Read(fs_user_data,FLASH_FS_VALID_ADDR,36);
				FileLengh = (fs_user_data[0] << 24) + (fs_user_data[1] << 16) + (fs_user_data[2] << 8) + fs_user_data[3];
				for(int i=0; i<32; i++) CloudMd5[i] = fs_user_data[4+i];
			
				FileLenHigh = FileLengh/4;
				FileLenLow = FileLengh%4;

						m_fs_init();
						m_fs_erase_user_app();

						for(int DownloadCnt = 0; DownloadCnt < FileLenHigh; DownloadCnt++)
						{
								W25QXX_Read(fs_user_data,FLASH_FS_APP_ADDR+FS_BUFF_SIZE*DownloadCnt,FS_BUFF_SIZE);
								if(m_fs_write_check(FS_APP_ADDR+FS_BUFF_SIZE*DownloadCnt,fs_user_data,FS_BUFF_SIZE) == true)
								{
										;
								}else
								{
//										W25QXX_PowerDown();
//										m_fs_uninit();
//										__disable_irq();
										NVIC_SystemReset();
								}
						}
						W25QXX_Read(fs_user_data, FLASH_FS_APP_ADDR+FS_BUFF_SIZE*FileLenHigh, FileLenLow);
						if(m_fs_write_check(FS_APP_ADDR+FS_BUFF_SIZE*FileLenHigh,fs_user_data,FileLenLow) == true)
						{
								;
						}else
						{
//								W25QXX_PowerDown();
//								m_fs_uninit();
//								__disable_irq();
								NVIC_SystemReset();
						}
						
						m_fs_erase_user_setting();
						for(uint16_t j=0; j<8092/FS_BUFF_SIZE; j++)
						{
								W25QXX_Read(fs_user_data,FLASH_FS_SETTING_ADDR+FS_BUFF_SIZE*j,FS_BUFF_SIZE);
								if(m_fs_write_check(FS_SETTING_ADDR+FS_BUFF_SIZE*j,fs_user_data,FS_BUFF_SIZE) == true)
								{
										;
								}else
								{
//										W25QXX_PowerDown();
//										m_fs_uninit();
//										__disable_irq();
										NVIC_SystemReset();
								}
						}
						W25QXX_Erase_Sector(FLASH_FS_VALID_SECTOR);

						W25QXX_PowerDown();
//						m_fs_uninit();
						__disable_irq();						
						NVIC_SystemReset();
				}
}

/**@brief Function for application main entry. */
int main(void)
{
    uint32_t ret_val;

		application_update();
	
    // Must happen before flash protection is applied, since it edits a protected page.
    nrf_bootloader_mbr_addrs_populate();
		
    // Protect MBR and bootloader code from being overwritten.
    ret_val = nrf_bootloader_flash_protect(0, MBR_SIZE);
    APP_ERROR_CHECK(ret_val);
   ret_val = nrf_bootloader_flash_protect(BOOTLOADER_START_ADDR, BOOTLOADER_SIZE);
//		ret_val = nrf_bootloader_flash_protect(BOOTLOADER_START_ADDR, 0x5000);
    APP_ERROR_CHECK(ret_val);

    (void) NRF_LOG_INIT(nrf_bootloader_dfu_timer_counter_get);
    NRF_LOG_DEFAULT_BACKENDS_INIT();
	
    NRF_LOG_INFO("Inside main");

    ret_val = nrf_bootloader_init(dfu_observer);
    APP_ERROR_CHECK(ret_val);

    NRF_LOG_FLUSH();

    NRF_LOG_ERROR("After main, should never be reached.");
    NRF_LOG_FLUSH();

    APP_ERROR_CHECK_BOOL(false);
}

/**
 * @}
 */

flash storage code:

#include "m_fstorage.h"
#include "nrf_soc.h"
#include "nrf_sdh.h"
#include "nrf_sdh_ble.h"
#include "nrf_fstorage.h"
#include "nrf_fstorage_sd.h"
#include "nrf_fstorage_nvmc.h"
#include "app_error.h"
#include "nrf_log.h"

static void fstorage_evt_handler(nrf_fstorage_evt_t * p_evt);

NRF_FSTORAGE_DEF(nrf_fstorage_t fstorage) =
{
    /* Set a handler for fstorage events. */
    .evt_handler = fstorage_evt_handler,

    /* These below are the boundaries of the flash space assigned to this instance of fstorage.
     * You must set these manually, even at runtime, before nrf_fstorage_init() is called.
     * The function nrf5_flash_end_addr_get() can be used to retrieve the last address on the
     * last page of flash available to write data. */
    .start_addr = FS_START_ADDR,
    .end_addr   = FS_END_ADDR,
};

/* Dummy data to write to flash. */


/**@brief   Helper function to obtain the last address on the last page of the on-chip flash that
 *          can be used to write user data.
 */
static uint32_t nrf5_flash_end_addr_get()
{
    uint32_t const bootloader_addr = BOOTLOADER_ADDRESS;
    uint32_t const page_sz         = NRF_FICR->CODEPAGESIZE;
    uint32_t const code_sz         = NRF_FICR->CODESIZE;

    return (bootloader_addr != 0xFFFFFFFF ?
            bootloader_addr : (code_sz * page_sz));
}

static void fstorage_evt_handler(nrf_fstorage_evt_t * p_evt)
{
    if (p_evt->result != NRF_SUCCESS)
    {
        NRF_LOG_INFO("--> Event received: ERROR while executing an fstorage operation.");
        return;
    }

    switch (p_evt->id)
    {
        case NRF_FSTORAGE_EVT_WRITE_RESULT:
        {
            NRF_LOG_INFO("--> Event received: wrote %d bytes at address 0x%x.",
                         p_evt->len, p_evt->addr);
        } break;

        case NRF_FSTORAGE_EVT_ERASE_RESULT:
        {
            NRF_LOG_INFO("--> Event received: erased %d page from address 0x%x.",
                         p_evt->len, p_evt->addr);
        } break;

        default:
            break;
    }
}

static void print_flash_info(nrf_fstorage_t * p_fstorage)
{
    NRF_LOG_INFO("========| flash info |========");
    NRF_LOG_INFO("erase unit: \t%d bytes",      p_fstorage->p_flash_info->erase_unit);
    NRF_LOG_INFO("program unit: \t%d bytes",    p_fstorage->p_flash_info->program_unit);
    NRF_LOG_INFO("==============================");
}

void wait_for_flash_ready(nrf_fstorage_t const * p_fstorage)
{
    /* While fstorage is busy, sleep and wait for an event. */
    while (nrf_fstorage_is_busy(p_fstorage))
    {
				#ifndef SOFTDEVICE_PRESENT
						(void) sd_app_evt_wait();
				#else
						__WFE();
				#endif
    }
}
/**@brief   flash storage init.
 *          
 */
void m_fs_init(void)
{
		ret_code_t rc;
    nrf_fstorage_api_t * p_fs_api;

#ifndef SOFTDEVICE_PRESENT
    NRF_LOG_INFO("SoftDevice is present.");
    NRF_LOG_INFO("Initializing nrf_fstorage_sd implementation...");
    /* Initialize an fstorage instance using the nrf_fstorage_sd backend.
     * nrf_fstorage_sd uses the SoftDevice to write to flash. This implementation can safely be
     * used whenever there is a SoftDevice, regardless of its status (enabled/disabled). */
    p_fs_api = &nrf_fstorage_sd;
#else
    NRF_LOG_INFO("SoftDevice not present.");
    NRF_LOG_INFO("Initializing nrf_fstorage_nvmc implementation...");
    /* Initialize an fstorage instance using the nrf_fstorage_nvmc backend.
     * nrf_fstorage_nvmc uses the NVMC peripheral. This implementation can be used when the
     * SoftDevice is disabled or not present.
     *
     * Using this implementation when the SoftDevice is enabled results in a hardfault. */
    p_fs_api = &nrf_fstorage_nvmc;
#endif

    rc = nrf_fstorage_init(&fstorage, p_fs_api, NULL);
    APP_ERROR_CHECK(rc);

    print_flash_info(&fstorage);

    /* It is possible to set the start and end addresses of an fstorage instance at runtime.
     * They can be set multiple times, should it be needed. The helper function below can
     * be used to determine the last address on the last page of flash memory available to
     * store data. */
    (void) nrf5_flash_end_addr_get();
}

/**@brief   flash storage uninit.
 *          
 */
void m_fs_uninit(void)
{
		ret_code_t rc;
    rc = nrf_fstorage_uninit(&fstorage, NULL);
    APP_ERROR_CHECK(rc);		
}

/**@brief   flash storage write data no check.
 *          
 */
void m_fs_write_nocheck(uint32_t w_addr, uint8_t *w_data, uint32_t w_len)
{
		ret_code_t rc;
    rc = nrf_fstorage_write(&fstorage, w_addr, w_data, w_len, NULL);
    APP_ERROR_CHECK(rc);
    wait_for_flash_ready(&fstorage);
    NRF_LOG_INFO("Write Done.");
}

/**@brief   flash storage write data with check.
 * @detail  w_len max size is FS_BUFF_SIZE
 */
bool m_fs_write_check(uint32_t w_addr, uint8_t *w_data, uint32_t w_len)
{
		ret_code_t rc;
		uint8_t r_data[FS_BUFF_SIZE]={0};
		
		if(w_len != 0)
		{
				/* Write data page*/
				rc = nrf_fstorage_write(&fstorage, w_addr, w_data, w_len, NULL);
				APP_ERROR_CHECK(rc);
				wait_for_flash_ready(&fstorage);
				NRF_LOG_INFO("Write Done.");
			
				/* Read data page*/
				rc = nrf_fstorage_read(&fstorage,w_addr,r_data,w_len);
				APP_ERROR_CHECK(rc);
				wait_for_flash_ready(&fstorage);
				
				if(memcmp(w_data,r_data,w_len)==0)
				{
						return true;
				}else
				{
					 return false;
				}
		}else
		{
				return false;
		}
}

/**@brief   flash storage read data.
 *          
 */
void m_fs_read(uint32_t r_addr, uint8_t *r_data, uint32_t r_len)
{
		ret_code_t rc;
		/* Read data page*/
		rc = nrf_fstorage_read(&fstorage,r_addr,r_data,r_len);
		APP_ERROR_CHECK(rc);
		wait_for_flash_ready(&fstorage);
}

/**@brief   flash storage erase data.
 *          
 */
//void m_fs_erase_backup_valid(void)
//{
//		ret_code_t rc;
//		/* Erase data page*/
//		rc = nrf_fstorage_erase(&fstorage,FS_BACKUP_VALID_ADDR,FS_VALID_SECTOR_LEN,NULL);
//		APP_ERROR_CHECK(rc);
//    wait_for_flash_ready(&fstorage);
//    NRF_LOG_INFO("Erase Done.");
//}
//void m_fs_erase_backup_app(void)
//{
//		ret_code_t rc;
//		/* Erase data page*/
//		rc = nrf_fstorage_erase(&fstorage,FS_BACKUP_APP_ADDR,FS_APP_SECTOR_LEN,NULL);
//		APP_ERROR_CHECK(rc);
//    wait_for_flash_ready(&fstorage);
//    NRF_LOG_INFO("Erase Done.");
//}
//void m_fs_erase_backup_setting(void)
//{
//		ret_code_t rc;
//		/* Erase data page*/
//		rc = nrf_fstorage_erase(&fstorage,FS_SETTING_ADDR,FS_SETTING_SECTOR_LEN,NULL);
//		APP_ERROR_CHECK(rc);
//    wait_for_flash_ready(&fstorage);
//    NRF_LOG_INFO("Erase Done.");
//}
void m_fs_erase_user_app(void)
{
		ret_code_t rc;
		/* Erase data page*/
		rc = nrf_fstorage_erase(&fstorage,FS_APP_ADDR,FS_APP_SECTOR_LEN,NULL);
		APP_ERROR_CHECK(rc);
    wait_for_flash_ready(&fstorage);
    NRF_LOG_INFO("Erase Done.");
}
void m_fs_erase_user_setting(void)
{
		ret_code_t rc;
		/* Erase data page*/
		rc = nrf_fstorage_erase(&fstorage,FS_SETTING_ADDR,FS_SETTING_SECTOR_LEN,NULL);
		APP_ERROR_CHECK(rc);
    wait_for_flash_ready(&fstorage);
    NRF_LOG_INFO("Erase Done.");
}

Related