/** @brief Function to continue Bootloader update * * @details This function will be called after reset if there is a valid Bootloader in Bank0 or Bank1 * required to be relocated and activated through MBR commands. * * @param[in] src_addr Source address of the BL to copy from. * @param[in] p_bank Pointer to the bank where the SoftDevice resides. * * @return This fucntion will not return if the bootloader is copied succesfully. * After the copy is verified the device will reset and start the new bootloader. * * @retval NRF_SUCCESS Continuation was successful. * @retval NRF_ERROR_INVALID_LENGTH Invalid length of flash operation. * @retval NRF_ERROR_NO_MEM if no parameter page is provided (see sds for more info). * @retval NRF_ERROR_INVALID_PARAM if an invalid command is given. * @retval NRF_ERROR_INTERNAL internal error that should not happen. * @retval NRF_ERROR_FORBIDDEN if NRF_UICR->BOOTADDR is not set. */ static uint32_t nrf_dfu_bl_continue(uint32_t src_addr, nrf_dfu_bank_t * p_bank) { uint32_t ret_val = NRF_SUCCESS; uint32_t const len = (p_bank->image_size - s_dfu_settings.sd_size); // if the update is a combination of BL + SD, offset with SD size to get BL start address src_addr += s_dfu_settings.sd_size; NRF_LOG_INFO("Verifying BL: Addr: 0x%08x, Src: 0x%08x, Len: 0x%08x\r\n", MAIN_APPLICATION_START_ADDR, src_addr, len); // If the bootloader is the same as the banked version, the copy is finished ret_val = nrf_dfu_mbr_compare((uint32_t*)BOOTLOADER_START_ADDR, (uint32_t*)src_addr, len); if (ret_val == NRF_SUCCESS) { NRF_LOG_INFO("Bootloader was verified\r\n"); // Invalidate bank, marking completion nrf_dfu_invalidate_bank(p_bank); (void)nrf_dfu_settings_write(NULL); } else { NRF_LOG_INFO("Bootloader not verified, copying: Src: 0x%08x, Len: 0x%08x\r\n", src_addr, len); // Bootloader is different than the banked version. Continue copy // Note that if the SD and BL was combined, then the split point between them is in s_dfu_settings.sd_size ret_val = nrf_dfu_mbr_copy_bl((uint32_t*)src_addr, len); if(ret_val != NRF_SUCCESS) { NRF_LOG_INFO("Request to copy BL failed\r\n"); } } return ret_val; }
void nrf_dfu_settings_init(void) { NRF_LOG_INFO("running nrf_dfu_settings_init\r\n"); uint32_t crc; flash_operation_pending = false; // Copy the DFU settings out of flash and into a buffer in RAM. memcpy((void*)&s_dfu_settings, &m_dfu_settings_buffer[0], sizeof(nrf_dfu_settings_t)); if(s_dfu_settings.crc != 0xFFFFFFFF) { // CRC is set. Content must be valid crc = nrf_dfu_settings_calculate_crc(); if(crc == s_dfu_settings.crc) { return; } } // Reached if nothing is configured or if CRC was wrong NRF_LOG_INFO("!!!!!!!!!!!!!!! Resetting bootloader settings !!!!!!!!!!!\r\n"); memset(&s_dfu_settings, 0x00, sizeof(nrf_dfu_settings_t)); s_dfu_settings.settings_version = NRF_DFU_SETTINGS_VERSION; (void)nrf_dfu_settings_write(NULL); }
/** @brief Function to continue SoftDevice update * * @details This function will be called after reset if there is a valid SoftDevice in Bank0 or Bank1 * required to be relocated and activated through MBR commands. * * @param[in] src_addr Source address of the SoftDevice to copy from. * @param[in] p_bank Pointer to the bank where the SoftDevice resides. * * @retval NRF_SUCCESS Continuation was successful. * @retval NRF_ERROR_INVALID_LENGTH Invalid len * @retval NRF_ERROR_NO_MEM if UICR.NRFFW[1] is not set (i.e. is 0xFFFFFFFF). * @retval NRF_ERROR_INVALID_PARAM if an invalid command is given. * @retval NRF_ERROR_INTERNAL indicates that the contents of the memory blocks where not verified correctly after copying. * @retval NRF_ERROR_NULL If the content of the memory blocks differs after copying. */ static uint32_t nrf_dfu_sd_continue(uint32_t src_addr, nrf_dfu_bank_t * p_bank) { uint32_t ret_val; ret_val = nrf_dfu_sd_continue_impl(src_addr, p_bank); if (ret_val != NRF_SUCCESS) { NRF_LOG_INFO("SD update continuation failed\r\n"); return ret_val; } nrf_dfu_invalidate_bank(p_bank); (void)nrf_dfu_settings_write(NULL); return ret_val; }
static void enter_bootloader(ble_dfu_t * p_dfu) { if (p_dfu->evt_handler != NULL) { ble_dfu_evt_t evt; evt.type = BLE_DFU_EVT_ENTERING_BOOTLOADER; p_dfu->evt_handler(p_dfu, &evt); } s_dfu_settings.enter_buttonless_dfu = true; (void)nrf_dfu_settings_write(flash_callback); /* TODO: - Save bond data */ }
void nrf_dfu_settings_init(bool sd_irq_initialized) { NRF_LOG_DEBUG("Running nrf_dfu_settings_init(sd_irq_initialized=%s).", sd_irq_initialized ? (uint32_t)"true" : (uint32_t)"false"); ret_code_t rc = nrf_dfu_flash_init(sd_irq_initialized); if (rc != NRF_SUCCESS) { NRF_LOG_ERROR("nrf_dfu_flash_init() failed with error: %x", rc); APP_ERROR_HANDLER(rc); } // Copy the DFU settings out of flash and into a buffer in RAM. memcpy((void*)&s_dfu_settings, m_dfu_settings_buffer, sizeof(nrf_dfu_settings_t)); if (s_dfu_settings.crc != 0xFFFFFFFF) { // CRC is set. Content must be valid uint32_t crc = nrf_dfu_settings_crc_get(); if (crc == s_dfu_settings.crc) { return; } } // Reached if the page is erased or CRC is wrong. NRF_LOG_DEBUG("Resetting bootloader settings."); memset(&s_dfu_settings, 0x00, sizeof(nrf_dfu_settings_t)); s_dfu_settings.settings_version = NRF_DFU_SETTINGS_VERSION; rc = nrf_dfu_settings_write(NULL); if (rc != NRF_SUCCESS) { NRF_LOG_ERROR("nrf_dfu_flash_write() failed with error: %x", rc); APP_ERROR_HANDLER(rc); } }
/** @brief Function to continue App update * * @details This function will be called after reset if there is a valid application in Bank1 * required to be copied down to bank0. * * @param[in] src_addr Source address of the application to copy from Bank1 to Bank0. * * @retval NRF_SUCCESS Continuation was successful. * @retval NRF_ERROR_NULL Invalid data during compare. * @retval FS_ERR_UNALIGNED_ADDR A call to fstorage was not aligned to a page boundary or the address was not word aliged. * @retval FS_ERR_INVALID_ADDR The destination of a call to fstorage does not point to * the start of a flash page or the operation would * go beyond the flash memory boundary. * @retval FS_ERR_NOT_INITIALIZED The fstorage module is not initialized. * @retval FS_ERR_INVALID_CFG The initialization of the fstorage module is invalid. * @retval FS_ERR_NULL_ARG A call to fstorage had an invalid NULL argument. * @retval FS_ERR_INVALID_ARG A call to fstorage had invalid arguments. * @retval FS_ERR_QUEUE_FULL If the internal operation queue of the fstorage module is full. * @retval FS_ERR_FAILURE_SINCE_LAST If an error occurred in another transaction and fstorage cannot continue before * the event has been dealt with. */ static uint32_t nrf_dfu_app_continue(uint32_t src_addr) { // This function only in use when new app is present in bank 1 uint32_t const image_size = s_dfu_settings.bank_1.image_size; uint32_t const split_size = CODE_PAGE_SIZE; // Arbitrary number that must be page aligned uint32_t ret_val = NRF_SUCCESS; uint32_t target_addr = MAIN_APPLICATION_START_ADDR + s_dfu_settings.write_offset; uint32_t length_left = (image_size - s_dfu_settings.write_offset); uint32_t cur_len; uint32_t crc; NRF_LOG_INFO("Enter nrf_dfu_app_continue\r\n"); // Copy the application down safely do { cur_len = (length_left > split_size) ? split_size : length_left; // Erase the target page ret_val = nrf_dfu_flash_erase((uint32_t*) target_addr, split_size / CODE_PAGE_SIZE, NULL); if (ret_val != NRF_SUCCESS) { return ret_val; } // Flash one page ret_val = nrf_dfu_flash_store((uint32_t*)target_addr, (uint32_t*)src_addr, cur_len, NULL); if (ret_val != NRF_SUCCESS) { return ret_val; } ret_val = nrf_dfu_mbr_compare((uint32_t*)target_addr, (uint32_t*)src_addr, cur_len); if (ret_val != NRF_SUCCESS) { // We will not retry the copy NRF_LOG_INFO("Invalid data during compare: target: 0x%08x, src: 0x%08x\r\n", target_addr, src_addr); return ret_val; } // Erase the head (to handle growing bank 0) ret_val = nrf_dfu_flash_erase((uint32_t*) src_addr, split_size / CODE_PAGE_SIZE, NULL); if (ret_val != NRF_SUCCESS) { NRF_LOG_INFO("App update: Failure erasing page at addr: 0x%08x\r\n", src_addr); return ret_val; } s_dfu_settings.write_offset += cur_len; (void)nrf_dfu_settings_write(NULL); target_addr += cur_len; src_addr += cur_len; length_left -= cur_len; } while(length_left > 0); // Check the crc of the copied data. Enable if so. crc = crc32_compute((uint8_t*)MAIN_APPLICATION_START_ADDR, image_size, NULL); if (crc == s_dfu_settings.bank_1.image_crc) { NRF_LOG_INFO("Setting app as valid\r\n"); s_dfu_settings.bank_0.bank_code = NRF_DFU_BANK_VALID_APP; s_dfu_settings.bank_0.image_crc = crc; s_dfu_settings.bank_0.image_size = image_size; } else { NRF_LOG_INFO("CRC computation failed for copied app: src crc: 0x%08x, res crc: 0x08x\r\n", s_dfu_settings.bank_1.image_crc, crc); } nrf_dfu_invalidate_bank(&s_dfu_settings.bank_1); (void)nrf_dfu_settings_write(NULL); return ret_val; }
/** @brief Function to execute the continuation of a SoftDevice update. * * @param[in] src_addr Source address of the SoftDevice to copy from. * @param[in] p_bank Pointer to the bank where the SoftDevice resides. * * @retval NRF_SUCCESS Continuation was successful. * @retval NRF_ERROR_INVALID_LENGTH Invalid len * @retval NRF_ERROR_NO_MEM if UICR.NRFFW[1] is not set (i.e. is 0xFFFFFFFF). * @retval NRF_ERROR_INVALID_PARAM if an invalid command is given. * @retval NRF_ERROR_INTERNAL indicates that the contents of the memory blocks where not verified correctly after copying. * @retval NRF_ERROR_NULL If the content of the memory blocks differs after copying. */ static uint32_t nrf_dfu_sd_continue_impl(uint32_t src_addr, nrf_dfu_bank_t * p_bank) { uint32_t ret_val = NRF_SUCCESS; uint32_t target_addr = SOFTDEVICE_REGION_START + s_dfu_settings.write_offset; uint32_t length_left = align_to_page(s_dfu_settings.sd_size - s_dfu_settings.write_offset, CODE_PAGE_SIZE); uint32_t split_size = align_to_page(length_left / 4, CODE_PAGE_SIZE); NRF_LOG_INFO("Enter nrf_bootloader_dfu_sd_continue\r\n"); // This can be a continuation due to a power failure src_addr += s_dfu_settings.write_offset; if (s_dfu_settings.sd_size != 0 && s_dfu_settings.write_offset == s_dfu_settings.sd_size) { NRF_LOG_INFO("SD already copied\r\n"); return NRF_SUCCESS; } NRF_LOG_INFO("Updating SD. Old SD ver: 0x%04x\r\n", SD_FWID_GET(MBR_SIZE)); do { NRF_LOG_INFO("Copying [0x%08x-0x%08x] to [0x%08x-0x%08x]: Len: 0x%08x\r\n", src_addr, src_addr + split_size, target_addr, target_addr + split_size, split_size); // Copy a chunk of the SD. Size in words ret_val = nrf_dfu_mbr_copy_sd((uint32_t*)target_addr, (uint32_t*)src_addr, split_size); if (ret_val != NRF_SUCCESS) { NRF_LOG_INFO("Failed to copy SD: target: 0x%08x, src: 0x%08x, len: 0x%08x\r\n", target_addr, src_addr, split_size); return ret_val; } NRF_LOG_INFO("Finished copying [0x%08x-0x%08x] to [0x%08x-0x%08x]: Len: 0x%08x\r\n", src_addr, src_addr + split_size, target_addr, target_addr + split_size, split_size); // Validate copy. Size in words ret_val = nrf_dfu_mbr_compare((uint32_t*)target_addr, (uint32_t*)src_addr, split_size); if (ret_val != NRF_SUCCESS) { NRF_LOG_INFO("Failed to Compare SD: target: 0x%08x, src: 0x%08x, len: 0x%08x\r\n", target_addr, src_addr, split_size); return ret_val; } NRF_LOG_INFO("Validated 0x%08x-0x%08x to 0x%08x-0x%08x: Size: 0x%08x\r\n", src_addr, src_addr + split_size, target_addr, target_addr + split_size, split_size); target_addr += split_size; src_addr += split_size; if (split_size > length_left) { length_left = 0; } else { length_left -= split_size; } NRF_LOG_INFO("Finished with the SD update.\r\n"); // Save the updated point of writes in case of power loss s_dfu_settings.write_offset = s_dfu_settings.sd_size - length_left; (void)nrf_dfu_settings_write(NULL); } while (length_left > 0); return ret_val; }