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