void bootloader_enable(void) { #ifdef RBC_MESH_SERIAL mesh_aci_start(); #endif bl_cmd_t enable_cmd; enable_cmd.type = BL_CMD_TYPE_ENABLE; bl_cmd_handler(&enable_cmd); transport_start(); /* Recover from broken state */ if (dfu_mesh_app_is_valid()) { set_timeout(TIMER_YIELD_TIMEOUT, TIMEOUT_ACTION_GO_TO_APP); } else { #ifdef RTT_LOG __LOG(RTT_CTRL_TEXT_RED "APP is invalid.\n"); bl_info_flags_t* p_flags = &bootloader_info_entry_get(BL_INFO_TYPE_FLAGS)->flags; bl_info_segment_t* p_seg = &bootloader_info_entry_get(BL_INFO_TYPE_SEGMENT_APP)->segment; __LOG("\tINTACT: SD: %d APP: %d BL: %d\n", p_flags->sd_intact, p_flags->app_intact, p_flags->bl_intact); if (*((uint32_t*) p_seg->start) == 0xFFFFFFFF) { __LOG("\tNo application at 0x%x\n", p_seg->start); } #endif /* update the bootloader if a bank is available */ if (dfu_bank_flash(DFU_TYPE_BOOTLOADER) == NRF_SUCCESS) { return; } dfu_type_t missing = dfu_mesh_missing_type_get(); if (missing != DFU_TYPE_NONE && dfu_bank_flash(missing) == NRF_ERROR_NOT_FOUND) { fwid_union_t req_fwid; bl_info_entry_t* p_fwid_entry = bootloader_info_entry_get(BL_INFO_TYPE_VERSION); APP_ERROR_CHECK_BOOL(p_fwid_entry != NULL); switch (missing) { case DFU_TYPE_SD: req_fwid.sd = p_fwid_entry->version.sd; break; case DFU_TYPE_APP: req_fwid.app = p_fwid_entry->version.app; break; default: APP_ERROR_CHECK(NRF_ERROR_INVALID_DATA); } dfu_mesh_req(missing, &req_fwid, (uint32_t*) 0xFFFFFFFF); } } }
uint32_t dfu_bank_flash(dfu_type_t dfu_type) { if (dfu_type != DFU_TYPE_SD && dfu_type != DFU_TYPE_APP && dfu_type != DFU_TYPE_BOOTLOADER) { return NRF_ERROR_INVALID_PARAM; } if (mp_bank_entry != NULL) { return NRF_ERROR_INVALID_STATE; } bl_info_entry_t* p_bank_entry = bootloader_info_entry_get((bl_info_type_t) (BL_INFO_TYPE_BANK_BASE + dfu_type)); if (!p_bank_entry || *p_bank_entry->bank.p_bank_addr == 0xFFFFFFFF) { return NRF_ERROR_NOT_FOUND; } __LOG("Flash bank of type %d\n", dfu_type); m_dfu_type = dfu_type; mp_bank_entry = &p_bank_entry->bank; flash_bank_entry(); return NRF_SUCCESS; }
void dfu_bank_scan(bool* p_bank_flash_started) { for (uint32_t i = 1; i <= 4; i <<= 1) { bl_info_entry_t* p_bank_entry = bootloader_info_entry_get((bl_info_type_t) (BL_INFO_TYPE_BANK_BASE + i)); if (!p_bank_entry) { continue; } if (p_bank_entry->bank.state != BL_INFO_BANK_STATE_IDLE) { __LOG("Flash of bank of type %d already started (state 0x%x), continue where we left off!\n", i, p_bank_entry->bank.state); m_dfu_type = (dfu_type_t) i; mp_bank_entry = &p_bank_entry->bank; flash_bank_entry(); if (p_bank_flash_started) { *p_bank_flash_started = true; } return; } } mp_bank_entry = NULL; if (p_bank_flash_started) { *p_bank_flash_started = false; } }
/***************************************************************************** * Interface Functions *****************************************************************************/ void bootloader_init(void) { mp_beacon = NULL; m_state = BL_STATE_FIND_FWID; m_transaction.transaction_id = 0; m_transaction.type = DFU_TYPE_NONE; memset(m_data_cache, 0xFF, DATA_CACHE_SIZE * sizeof(uint16_t)); memset(m_req_cache, 0, REQ_CACHE_SIZE * sizeof(m_req_cache[0])); memset(m_tid_cache, 0, TRANSACTION_ID_CACHE_SIZE); m_tid_index = 0; m_data_index = 0; m_req_index = 0; /* fetch persistent entries */ m_bl_info_pointers.p_flags = &bootloader_info_entry_get((uint32_t*) BOOTLOADER_INFO_ADDRESS, BL_INFO_TYPE_FLAGS)->flags; m_bl_info_pointers.p_fwid = &bootloader_info_entry_get((uint32_t*) BOOTLOADER_INFO_ADDRESS, BL_INFO_TYPE_VERSION)->version; m_bl_info_pointers.p_segment_app = &bootloader_info_entry_get((uint32_t*) BOOTLOADER_INFO_ADDRESS, BL_INFO_TYPE_SEGMENT_APP)->segment; m_bl_info_pointers.p_segment_bl = &bootloader_info_entry_get((uint32_t*) BOOTLOADER_INFO_ADDRESS, BL_INFO_TYPE_SEGMENT_BL)->segment; m_bl_info_pointers.p_segment_sd = &bootloader_info_entry_get((uint32_t*) BOOTLOADER_INFO_ADDRESS, BL_INFO_TYPE_SEGMENT_SD)->segment; m_bl_info_pointers.p_ecdsa_public_key = &bootloader_info_entry_get((uint32_t*) BOOTLOADER_INFO_ADDRESS, BL_INFO_TYPE_ECDSA_PUBLIC_KEY)->public_key[0]; if ( ((uint32_t) m_bl_info_pointers.p_flags < BOOTLOADER_INFO_ADDRESS) || ((uint32_t) m_bl_info_pointers.p_fwid < BOOTLOADER_INFO_ADDRESS) || ((uint32_t) m_bl_info_pointers.p_segment_app < BOOTLOADER_INFO_ADDRESS) || ((uint32_t) m_bl_info_pointers.p_segment_sd < BOOTLOADER_INFO_ADDRESS) || ((uint32_t) m_bl_info_pointers.p_segment_bl < BOOTLOADER_INFO_ADDRESS) ) { bootloader_abort(BL_END_ERROR_INVALID_PERSISTENT_STORAGE); } }
void bootloader_abort(dfu_end_t end_reason) { __LOG("ABORT...\n"); bl_info_entry_t* p_segment_entry = bootloader_info_entry_get(BL_INFO_TYPE_SEGMENT_APP); switch (end_reason) { case DFU_END_SUCCESS: case DFU_END_ERROR_TIMEOUT: case DFU_END_FWID_VALID: case DFU_END_ERROR_MBR_CALL_FAILED: if (p_segment_entry && dfu_mesh_app_is_valid()) { if (fifo_is_empty(&m_flash_fifo)) { interrupts_disable(); sd_mbr_command_t com = {SD_MBR_COMMAND_INIT_SD, }; uint32_t err_code = sd_mbr_command(&com); APP_ERROR_CHECK(err_code); err_code = sd_softdevice_vector_table_base_set(p_segment_entry->segment.start); APP_ERROR_CHECK(err_code); #ifdef DEBUG_LEDS NRF_GPIO->OUTSET = (1 << 21) | (1 << 22) | (1 << 23) | (1 << 24); #endif bootloader_util_app_start(p_segment_entry->segment.start); } else { __LOG("->Will go to app once flash is finished.\n"); m_go_to_app = true; } } else if (p_segment_entry) { __LOG("->App not valid.\n"); } else { __LOG("->No segment entry found\n"); } break; case DFU_END_ERROR_INVALID_PERSISTENT_STORAGE: APP_ERROR_CHECK_BOOL(false); default: __LOG(RTT_CTRL_TEXT_RED "SYSTEM RESET (reason 0x%x)\n", end_reason); __disable_irq(); while(1); //NVIC_SystemReset(); } }
/** Interrupt handling Flash operations. */ void FLASH_HANDLER_IRQHandler(void) { flash_queue_entry_t flash_entry; uint32_t op_count = 0; while (fifo_pop(&m_flash_fifo, &flash_entry) == NRF_SUCCESS) { op_count++; bl_cmd_t rsp_cmd; if (flash_entry.type == FLASH_OP_TYPE_WRITE) { APP_ERROR_CHECK_BOOL(IS_WORD_ALIGNED(flash_entry.op.write.start_addr)); APP_ERROR_CHECK_BOOL(IS_WORD_ALIGNED(flash_entry.op.write.length)); APP_ERROR_CHECK_BOOL(IS_WORD_ALIGNED(flash_entry.op.write.p_data)); __LOG("WRITING to 0x%x.(len %d)\n", flash_entry.op.write.start_addr, flash_entry.op.write.length); if (flash_entry.op.write.start_addr >= 0x20000000) { uint8_t* p_dst = ((uint8_t*) flash_entry.op.write.start_addr); for (uint32_t i = 0; i < flash_entry.op.write.length; ++i, p_dst++) { *p_dst = (*p_dst & flash_entry.op.write.p_data[i]); } } else { nrf_flash_store((uint32_t*) flash_entry.op.write.start_addr, flash_entry.op.write.p_data, flash_entry.op.write.length, 0); } rsp_cmd.type = BL_CMD_TYPE_FLASH_WRITE_COMPLETE; rsp_cmd.params.flash.write.p_data = flash_entry.op.write.p_data; } else { __LOG("ERASING 0x%x.\n", flash_entry.op.erase.start_addr); if (flash_entry.op.erase.start_addr >= 0x20000000) { memset((uint32_t*) flash_entry.op.erase.start_addr, 0xFF, flash_entry.op.erase.length); } else { nrf_flash_erase((uint32_t*) flash_entry.op.erase.start_addr, flash_entry.op.erase.length); } rsp_cmd.type = BL_CMD_TYPE_FLASH_ERASE_COMPLETE; rsp_cmd.params.flash.erase.p_dest = (uint32_t*) flash_entry.op.erase.start_addr; } bl_cmd_handler(&rsp_cmd); } if (op_count > 0) { bl_cmd_t idle_cmd; idle_cmd.type = BL_CMD_TYPE_FLASH_ALL_COMPLETE; bl_cmd_handler(&idle_cmd); } if (fifo_is_empty(&m_flash_fifo) && m_go_to_app) { bl_info_entry_t* p_segment_entry = bootloader_info_entry_get(BL_INFO_TYPE_SEGMENT_APP); bootloader_util_app_start(p_segment_entry->segment.start); } }
static void flash_bank_entry(void) { bl_info_bank_t* p_bank_entry = mp_bank_entry; /* make local copy to avoid race conditions */ if (p_bank_entry == NULL) { return; } bl_info_entry_t bank_entry_replacement; memcpy(&bank_entry_replacement, p_bank_entry, sizeof(bl_info_bank_t)); switch (p_bank_entry->state) { case BL_INFO_BANK_STATE_IDLE: { m_waiting_for_idle = true; bank_entry_replacement.bank.state = BL_INFO_BANK_STATE_FLASH_FW; bootloader_info_entry_overwrite((bl_info_type_t) (BL_INFO_TYPE_BANK_BASE + m_dfu_type), &bank_entry_replacement); /* Wait for this to take effect before moving on, as the potential mbr commands in the flash_fw state may trigger sudden reboots. */ return; } case BL_INFO_BANK_STATE_FLASH_FW: switch (m_dfu_type) { case DFU_TYPE_BOOTLOADER: /* Check to see if the bank transfer has been executed */ if (memcmp(p_bank_entry->p_bank_addr, (uint32_t*) bootloader_info_entry_get(BL_INFO_TYPE_SEGMENT_BL)->segment.start, p_bank_entry->length) != 0) { /* move the bank with MBR. BOOTLOADERADDR() must have been set. */ sd_mbr_command_t sd_mbr_cmd; sd_mbr_cmd.command = SD_MBR_COMMAND_COPY_BL; sd_mbr_cmd.params.copy_bl.bl_src = p_bank_entry->p_bank_addr; sd_mbr_cmd.params.copy_bl.bl_len = p_bank_entry->length / sizeof(uint32_t); APP_ERROR_CHECK(sd_mbr_command(&sd_mbr_cmd)); return; /* Can't be reached, only here for readability. */ } else { bank_entry_replacement.bank.state = BL_INFO_BANK_STATE_FLASH_META; bootloader_info_entry_overwrite(BL_INFO_TYPE_BANK_BL, &bank_entry_replacement); } break; case DFU_TYPE_SD: /* Check to see if the bank transfer has been executed */ if (memcmp(p_bank_entry->p_bank_addr, (uint32_t*) bootloader_info_entry_get(BL_INFO_TYPE_SEGMENT_SD)->segment.start, p_bank_entry->length) != 0) { /* move the bank with MBR. */ sd_mbr_command_t sd_mbr_cmd; sd_mbr_cmd.command = SD_MBR_COMMAND_COPY_SD; sd_mbr_cmd.params.copy_sd.src = p_bank_entry->p_bank_addr; sd_mbr_cmd.params.copy_sd.len = p_bank_entry->length / sizeof(uint32_t); sd_mbr_cmd.params.copy_sd.dst = (uint32_t*) 0x1000; APP_ERROR_CHECK(sd_mbr_command(&sd_mbr_cmd)); return; /* Can't be reached, only here for readability. */ } else { bank_entry_replacement.bank.state = BL_INFO_BANK_STATE_FLASH_META; bootloader_info_entry_overwrite((bl_info_type_t) (BL_INFO_TYPE_BANK_BASE + m_dfu_type), &bank_entry_replacement); } break; case DFU_TYPE_APP: /* This nukes the call stack and any flash-callbacks on the app side. If we're in the application, we have to jump to bootloader. */ if (bootloader_is_in_application()) { /* All paths leading to this call warns about this reset. We'll come back to finalize the transfer after the reset. */ __LOG("IN APP MODE. RESET!\n"); #if 1 //def SOFTDEVICE_PRESENT sd_power_reset_reason_clr(0x0F000F); #if NORDIC_SDK_VERSION >= 11 sd_power_gpregret_set(0, RBC_MESH_GPREGRET_CODE_GO_TO_APP); #else sd_power_gpregret_set(RBC_MESH_GPREGRET_CODE_GO_TO_APP); #endif sd_nvic_SystemReset(); #else NRF_POWER->RESETREAS = 0x0F000F; /* erase reset-reason to avoid wrongful state-readout on reboot */ NRF_POWER->GPREGRET = RBC_MESH_GPREGRET_CODE_GO_TO_APP; NVIC_SystemReset(); #endif } else { /* Erase, Flash the FW, flash FW flag, flash the signature, erase the bank entry. */ bl_info_entry_t* p_app_entry = bootloader_info_entry_get(BL_INFO_TYPE_SEGMENT_APP); APP_ERROR_CHECK_BOOL(p_app_entry != NULL); APP_ERROR_CHECK_BOOL(IS_PAGE_ALIGNED(p_app_entry->segment.start)); /* Erase existing FW */ bl_evt_t flash_evt; flash_evt.type = BL_EVT_TYPE_FLASH_ERASE; flash_evt.params.flash.erase.start_addr = p_app_entry->segment.start; flash_evt.params.flash.erase.length = ((p_bank_entry->length + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1)); /* Pad the rest of the page */ if (bootloader_evt_send(&flash_evt) != NRF_SUCCESS) { m_waiting_for_idle = true; return; } /* Flash bank */ flash_evt.type = BL_EVT_TYPE_FLASH_WRITE; flash_evt.params.flash.write.p_data = (uint8_t*) p_bank_entry->p_bank_addr; flash_evt.params.flash.write.length = p_bank_entry->length; flash_evt.params.flash.write.start_addr = p_app_entry->segment.start; if (bootloader_evt_send(&flash_evt) != NRF_SUCCESS) { m_waiting_for_idle = true; return; } /* Update state */ bank_entry_replacement.bank.state = BL_INFO_BANK_STATE_FLASH_META; bootloader_info_entry_overwrite((bl_info_type_t) (BL_INFO_TYPE_BANK_BASE + m_dfu_type), &bank_entry_replacement); } break; default: APP_ERROR_CHECK(NRF_ERROR_INVALID_DATA); break; } /* deliberate fallthrough */ case BL_INFO_BANK_STATE_FLASH_META: { bl_info_entry_t fwid_entry; bl_info_type_t signature_type; bl_info_entry_t flags_entry; bl_info_entry_t* p_old_fwid_entry = bootloader_info_entry_get(BL_INFO_TYPE_VERSION); bl_info_entry_t* p_old_flags_entry = bootloader_info_entry_get(BL_INFO_TYPE_FLAGS); APP_ERROR_CHECK_BOOL(p_old_fwid_entry); APP_ERROR_CHECK_BOOL(p_bank_entry); memcpy(&fwid_entry, p_old_fwid_entry, sizeof(bl_info_version_t)); memcpy(&flags_entry, p_old_flags_entry, sizeof(bl_info_flags_t)); switch (m_dfu_type) { case DFU_TYPE_SD: fwid_entry.version.sd = p_bank_entry->fwid.sd; signature_type = BL_INFO_TYPE_SIGNATURE_SD; flags_entry.flags.sd_intact = true; break; case DFU_TYPE_BOOTLOADER: fwid_entry.version.bootloader.id = p_bank_entry->fwid.bootloader.id; fwid_entry.version.bootloader.ver = p_bank_entry->fwid.bootloader.ver; signature_type = BL_INFO_TYPE_SIGNATURE_BL; flags_entry.flags.bl_intact = true; break; case DFU_TYPE_APP: fwid_entry.version.app.company_id = p_bank_entry->fwid.app.company_id; fwid_entry.version.app.app_id = p_bank_entry->fwid.app.app_id; fwid_entry.version.app.app_version = p_bank_entry->fwid.app.app_version; signature_type = BL_INFO_TYPE_SIGNATURE_APP; flags_entry.flags.app_intact = true; break; default: APP_ERROR_CHECK(NRF_ERROR_INVALID_DATA); return; } if (!bootloader_info_entry_put(BL_INFO_TYPE_VERSION, &fwid_entry, BL_INFO_LEN_FWID)) { m_waiting_for_idle = true; return; } if (p_bank_entry->has_signature) { if (!bootloader_info_entry_put(signature_type, (bl_info_entry_t*) p_bank_entry->signature, BL_INFO_LEN_SIGNATURE)) { m_waiting_for_idle = true; return; } } if (!bootloader_info_entry_put(BL_INFO_TYPE_FLAGS, &flags_entry, BL_INFO_LEN_FLAGS)) { m_waiting_for_idle = true; return; } /* Update state */ __LOG("Bank: Set state to FLASHED\n"); bank_entry_replacement.bank.state = BL_INFO_BANK_STATE_FLASHED; bootloader_info_entry_overwrite((bl_info_type_t) (BL_INFO_TYPE_BANK_BASE + m_dfu_type), &bank_entry_replacement); } /* deliberate fallthrough */ case BL_INFO_BANK_STATE_FLASHED: /* We may invalidate the bank entry in the device page now, it's all redundant. */ __LOG("Bank: Invalidate.\n"); if (bootloader_info_entry_invalidate((bl_info_type_t) (BL_INFO_TYPE_BANK_BASE + m_dfu_type)) == NRF_SUCCESS) { __LOG("Bank invalidated.\n"); mp_bank_entry = NULL; /* reset the static bank pointer, as we no longer need it. */ } else { m_waiting_for_idle = true; } break; } }
bool dfu_bank_is_available(dfu_type_t dfu_type) { bl_info_entry_t* p_entry = bootloader_info_entry_get((bl_info_type_t) (BL_INFO_TYPE_BANK_BASE + dfu_type)); return (p_entry && p_entry->bank.state == BL_INFO_BANK_STATE_IDLE); }