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.");
}

Parents
  • Hi

    We don't have any SPI implementations for the bootloader in the nRF5 SDK I'm afraid, but porting the UART bootloader to SPI should be possible. You'd have to change the transport layer of the UART bootloader on your end which shouldn't be too much work.

    What kind of errors are you running into here exactly? Hard to give any specific suggestions without knowing what error codes are being returned from what functions.

    Best regards,

    Simon

Reply
  • Hi

    We don't have any SPI implementations for the bootloader in the nRF5 SDK I'm afraid, but porting the UART bootloader to SPI should be possible. You'd have to change the transport layer of the UART bootloader on your end which shouldn't be too much work.

    What kind of errors are you running into here exactly? Hard to give any specific suggestions without knowing what error codes are being returned from what functions.

    Best regards,

    Simon

Children
No Data
Related