/***************************************************************************** * 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_timeout(void) { __LOG("BL: TIMEOUT! ACTION: %s\n", mp_timeout_action_strs[(uint32_t) m_timeout_action]); bl_cmd_t cmd; switch (m_timeout_action) { case TIMEOUT_ACTION_DFU_TIMEOUT: cmd.type = BL_CMD_TYPE_TIMEOUT; cmd.params.timeout.timer_index = 0; break; case TIMEOUT_ACTION_DFU_ABORT: cmd.type = BL_CMD_TYPE_DFU_ABORT; break; case TIMEOUT_ACTION_GO_TO_APP: bootloader_abort(DFU_END_SUCCESS); return; default: APP_ERROR_CHECK(NRF_ERROR_INVALID_STATE); } m_timeout_action = TIMEOUT_ACTION_NONE; bootloader_cmd_send(&cmd); }
int main(void) { init_clock(); NVIC_SetPriority(SWI2_IRQn, 2); NVIC_EnableIRQ(SWI2_IRQn); __enable_irq(); #ifdef RTT_LOG SEGGER_RTT_Init(); __LOG("= START | %s | ===========================================================\n", __TIME__); #endif init_leds(); bootloader_init(); /* Wait for any ongoing bank transfers to finish. */ while (dfu_bank_transfer_in_progress()) { /* may safely while-loop here, as the bank-transfer finishes in an IRQ. */ __WFE(); } /* check whether we should go to application */ if (NRF_POWER->GPREGRET == RBC_MESH_GPREGRET_CODE_GO_TO_APP) { bootloader_abort(DFU_END_SUCCESS); } NRF_POWER->GPREGRET = RBC_MESH_GPREGRET_CODE_GO_TO_APP; bootloader_enable(); while (1) { __WFE(); } }
static dfu_packet_t* beacon_set(beacon_type_t type) { if (mp_beacon) { transport_tx_abort(mp_beacon); if (!mesh_packet_ref_count_dec(mp_beacon)) { #ifdef DEBUG_LEDS NRF_GPIO->OUTCLR = (1 << 24); #endif } } if (!mesh_packet_acquire(&mp_beacon)) { bootloader_abort(BL_END_ERROR_NO_MEM); } dfu_packet_t* p_dfu = (dfu_packet_t*) &(((ble_ad_t*) mp_beacon->payload)->data[2]); memset(p_dfu, 0, sizeof(dfu_packet_t)); uint8_t repeats = TX_REPEATS_FWID; tx_interval_type_t interval_type = TX_INTERVAL_TYPE_FWID; if (type >= BEACON_TYPE_READY_APP && type <= BEACON_TYPE_READY_BL) { p_dfu->packet_type = DFU_PACKET_TYPE_STATE; p_dfu->payload.state.authority = m_transaction.authority; p_dfu->payload.state.flood = m_transaction.flood; p_dfu->payload.state.transaction_id = m_transaction.transaction_id; p_dfu->payload.state.relay_node = (m_state == BL_STATE_RELAY_CANDIDATE); p_dfu->payload.state.fwid = m_transaction.target_fwid_union; repeats = TX_REPEATS_READY; interval_type = TX_INTERVAL_TYPE_READY; } switch (type) { case BEACON_TYPE_FWID: bootloader_packet_set_local_fields(mp_beacon, DFU_PACKET_LEN_FWID); p_dfu->packet_type = DFU_PACKET_TYPE_FWID; p_dfu->payload.fwid = *m_bl_info_pointers.p_fwid; break; case BEACON_TYPE_READY_APP: bootloader_packet_set_local_fields(mp_beacon, DFU_PACKET_LEN_STATE_APP); p_dfu->payload.state.dfu_type = DFU_TYPE_APP; break; case BEACON_TYPE_READY_SD: bootloader_packet_set_local_fields(mp_beacon, DFU_PACKET_LEN_STATE_SD); p_dfu->payload.state.dfu_type = DFU_TYPE_SD; break; case BEACON_TYPE_READY_BL: bootloader_packet_set_local_fields(mp_beacon, DFU_PACKET_LEN_STATE_BL); p_dfu->payload.state.dfu_type = DFU_TYPE_BOOTLOADER; break; } transport_tx(mp_beacon, repeats, interval_type, NULL); return p_dfu; }
void bootloader_rtc_irq_handler(void) { NRF_RTC0->INTENCLR = (1 << (RTC_BL_STATE_CH + RTC_INTENCLR_COMPARE0_Pos)); switch (m_state) { case BL_STATE_FIND_FWID: bootloader_abort(BL_END_FWID_VALID); break; case BL_STATE_DFU_REQ: case BL_STATE_DFU_READY: bootloader_abort(BL_END_ERROR_NO_START); break; case BL_STATE_DFU_TARGET: start_req(m_transaction.type, true); break; case BL_STATE_VALIDATE: if (signature_check()) { /* Don't want any interrupts disturbing this final stage */ uint32_t was_masked; _DISABLE_IRQS(was_masked); /* write new version in bl info: */ bl_info_entry_t new_version_entry; memcpy(&new_version_entry.version, m_bl_info_pointers.p_fwid, sizeof(fwid_t)); bl_info_type_t sign_info_type = BL_INFO_TYPE_INVALID; if (m_bl_info_pointers.p_flags == NULL) { APP_ERROR_CHECK(NRF_ERROR_NULL); } /* copy flags, then mark the type we just verified as intact before reflashing it. */ bl_info_entry_t flags_entry; memcpy(&flags_entry, m_bl_info_pointers.p_flags, ((BL_INFO_LEN_FLAGS + 3) & ~0x03UL)); switch (m_transaction.type) { case DFU_TYPE_APP: memcpy((void*) &new_version_entry.version.app, (void*) &m_transaction.target_fwid_union.app, DFU_FWID_LEN_APP); sign_info_type = BL_INFO_TYPE_SIGNATURE_APP; flags_entry.flags.app_intact = true; break; case DFU_TYPE_SD: memcpy((void*) &new_version_entry.version.sd, (void*) &m_transaction.target_fwid_union.sd, DFU_FWID_LEN_SD); sign_info_type = BL_INFO_TYPE_SIGNATURE_SD; flags_entry.flags.sd_intact = true; break; case DFU_TYPE_BOOTLOADER: memcpy((void*) &new_version_entry.version.bootloader, (void*) &m_transaction.target_fwid_union.bootloader, DFU_FWID_LEN_BL); sign_info_type = BL_INFO_TYPE_SIGNATURE_BL; flags_entry.flags.bl_intact = true; break; default: break; } m_bl_info_pointers.p_fwid = &bootloader_info_entry_put(BL_INFO_TYPE_VERSION, &new_version_entry, BL_INFO_LEN_FWID)->version; m_bl_info_pointers.p_flags = &bootloader_info_entry_put(BL_INFO_TYPE_FLAGS, &flags_entry, BL_INFO_LEN_FLAGS)->flags; /* add signature to bl info, if applicable: */ if (m_transaction.signature_length != 0) { bootloader_info_entry_put(sign_info_type, (bl_info_entry_t*) m_transaction.signature, DFU_SIGNATURE_LEN); } _ENABLE_IRQS(was_masked); bootloader_abort(BL_END_SUCCESS); } else { /* someone gave us anauthorized firmware, and we're broken. need to reboot and try to request a new transfer */ bootloader_abort(BL_END_ERROR_UNAUTHORIZED); } break; case BL_STATE_RELAY: case BL_STATE_RELAY_CANDIDATE: bootloader_abort(BL_END_SUCCESS); break; default: break; } }
static uint32_t bl_evt_handler(bl_evt_t* p_evt) { static bl_cmd_t rsp_cmd; bool respond = false; switch (p_evt->type) { case BL_EVT_TYPE_DFU_ABORT: bootloader_abort(p_evt->params.dfu.abort.reason); /* If bootloader abort returned, it means that the application * doesn't work, and we should return to the dfu operation. */ dfu_mesh_start(); break; case BL_EVT_TYPE_TX_RADIO: { mesh_packet_t* p_packet = NULL; if (!mesh_packet_acquire(&p_packet)) { return NRF_ERROR_NO_MEM; } mesh_packet_set_local_addr(p_packet); p_packet->header.type = BLE_PACKET_TYPE_ADV_NONCONN_IND; p_packet->header.length = DFU_PACKET_OVERHEAD + p_evt->params.tx.radio.length; ((ble_ad_t*) p_packet->payload)->adv_data_type = MESH_ADV_DATA_TYPE; ((ble_ad_t*) p_packet->payload)->data[0] = (MESH_UUID & 0xFF); ((ble_ad_t*) p_packet->payload)->data[1] = (MESH_UUID >> 8) & 0xFF; ((ble_ad_t*) p_packet->payload)->adv_data_length = DFU_PACKET_ADV_OVERHEAD + p_evt->params.tx.radio.length; memcpy(&p_packet->payload[4], p_evt->params.tx.radio.p_dfu_packet, p_evt->params.tx.radio.length); bool success = transport_tx(p_packet, p_evt->params.tx.radio.tx_slot, p_evt->params.tx.radio.tx_count, (tx_interval_type_t) p_evt->params.tx.radio.interval_type); mesh_packet_ref_count_dec(p_packet); if (!success) { return NRF_ERROR_INTERNAL; } break; } case BL_EVT_TYPE_TX_ABORT: transport_tx_abort(p_evt->params.tx.abort.tx_slot); break; case BL_EVT_TYPE_TX_SERIAL: { #ifdef RBC_MESH_SERIAL serial_evt_t serial_evt; serial_evt.opcode = SERIAL_EVT_OPCODE_DFU; memcpy(&serial_evt.params.dfu.packet, p_evt->params.tx.serial.p_dfu_packet, p_evt->params.tx.serial.length); serial_evt.length = SERIAL_PACKET_OVERHEAD + p_evt->params.tx.serial.length; if (!serial_handler_event_send(&serial_evt)) { return NRF_ERROR_INTERNAL; } break; #endif } case BL_EVT_TYPE_TIMER_SET: set_timeout(US_TO_RTC_TICKS(p_evt->params.timer.set.delay_us), TIMEOUT_ACTION_DFU_TIMEOUT); break; case BL_EVT_TYPE_DFU_NEW_FW: { stop_timeout(); __LOG("New FW event\n"); switch (p_evt->params.dfu.new_fw.fw_type) { case DFU_TYPE_APP: __LOG("\tAPP: %08x.%04x:%08x\n", (uint32_t) p_evt->params.dfu.new_fw.fwid.app.company_id, (uint32_t) p_evt->params.dfu.new_fw.fwid.app.app_id, (uint32_t) p_evt->params.dfu.new_fw.fwid.app.app_version); break; case DFU_TYPE_SD: __LOG("\tSD: %04x\n", (uint32_t) p_evt->params.dfu.new_fw.fwid.sd); break; case DFU_TYPE_BOOTLOADER: __LOG("\tBL: %02x:%02x\n", (uint32_t) p_evt->params.dfu.new_fw.fwid.bootloader.id, (uint32_t) p_evt->params.dfu.new_fw.fwid.bootloader.ver); break; default: break; } /* accept all new firmware, as the bootloader wouldn't run unless there's an actual reason for it. */ rsp_cmd.type = BL_CMD_TYPE_DFU_START_TARGET; rsp_cmd.params.dfu.start.target.p_bank_start = (uint32_t*) 0xFFFFFFFF; /* no banking */ rsp_cmd.params.dfu.start.target.type = p_evt->params.dfu.new_fw.fw_type; rsp_cmd.params.dfu.start.target.fwid = p_evt->params.dfu.new_fw.fwid; respond = true; } break; case BL_EVT_TYPE_BANK_AVAILABLE: __LOG("Bank:\n"); switch (p_evt->params.bank_available.bank_dfu_type) { case DFU_TYPE_APP: __LOG("\tAPP: %08x.%04x:%08x\n", (uint32_t) p_evt->params.bank_available.bank_fwid.app.company_id, (uint32_t) p_evt->params.bank_available.bank_fwid.app.app_id, (uint32_t) p_evt->params.bank_available.bank_fwid.app.app_version); break; case DFU_TYPE_SD: __LOG("\tSD: %04x\n", (uint32_t) p_evt->params.bank_available.bank_fwid.sd); break; case DFU_TYPE_BOOTLOADER: __LOG("\tBL: %02x:%02x\n", (uint32_t) p_evt->params.bank_available.bank_fwid.bootloader.id, (uint32_t) p_evt->params.bank_available.bank_fwid.bootloader.ver); break; default: break; } __LOG("\tLocation: 0x%x\n", p_evt->params.bank_available.p_bank_addr); __LOG("\tLength: 0x%x\n", p_evt->params.bank_available.bank_length); if (p_evt->params.bank_available.bank_dfu_type == DFU_TYPE_BOOTLOADER) { if (!dfu_mesh_app_is_valid()) { dfu_bank_flash(DFU_TYPE_BOOTLOADER); } } break; case BL_EVT_TYPE_DFU_REQ: { /* Always attempt to relay incoming transfers in BL mode. Will not abort ongoing transfers. */ if (p_evt->params.dfu.req.role == DFU_ROLE_RELAY) { stop_timeout(); bl_cmd_t relay_cmd; relay_cmd.type = BL_CMD_TYPE_DFU_START_RELAY; relay_cmd.params.dfu.start.relay.fwid = p_evt->params.dfu.req.fwid; relay_cmd.params.dfu.start.relay.type = p_evt->params.dfu.req.dfu_type; relay_cmd.params.dfu.start.relay.transaction_id = p_evt->params.dfu.req.transaction_id; bootloader_cmd_send(&relay_cmd); } } break; case BL_EVT_TYPE_DFU_START: set_timeout(TIMER_START_TIMEOUT, TIMEOUT_ACTION_DFU_ABORT); break; case BL_EVT_TYPE_DFU_DATA_SEGMENT_RX: __LOG("RX %u/%u\n", p_evt->params.dfu.data_segment.received_segment, p_evt->params.dfu.data_segment.total_segments); set_timeout(TIMER_DATA_TIMEOUT, TIMEOUT_ACTION_DFU_ABORT); break; case BL_EVT_TYPE_DFU_END: if (p_evt->params.dfu.end.dfu_type == DFU_TYPE_APP || p_evt->params.dfu.end.dfu_type == DFU_TYPE_SD) { /* attempt to reboot to app */ bootloader_abort(DFU_END_SUCCESS); } break; /* Defer the flash operations to an asynchronous handler. Doing it inline causes stack overflow, as the bootloader continues in the response callback. */ case BL_EVT_TYPE_FLASH_WRITE: { if (!IS_WORD_ALIGNED(p_evt->params.flash.write.start_addr) || !IS_WORD_ALIGNED(p_evt->params.flash.write.p_data)) { return NRF_ERROR_INVALID_ADDR; } if (!IS_WORD_ALIGNED(p_evt->params.flash.write.length)) { return NRF_ERROR_INVALID_LENGTH; } if ((p_evt->params.flash.write.start_addr + p_evt->params.flash.write.length) > NRF_UICR->BOOTLOADERADDR && p_evt->params.flash.write.start_addr < 0x3f800) { APP_ERROR_CHECK(NRF_ERROR_INVALID_ADDR); } flash_queue_entry_t queue_entry; queue_entry.type = FLASH_OP_TYPE_WRITE; memcpy(&queue_entry.op, &p_evt->params.flash, sizeof(flash_op_t)); if (fifo_push(&m_flash_fifo, &queue_entry) != NRF_SUCCESS) { __LOG(RTT_CTRL_TEXT_RED "FLASH FIFO FULL :( Increase the fifo size.\n"); return NRF_ERROR_NO_MEM; } NVIC_SetPendingIRQ(FLASH_HANDLER_IRQn); } break; case BL_EVT_TYPE_FLASH_ERASE: { flash_queue_entry_t queue_entry; queue_entry.type = FLASH_OP_TYPE_ERASE; memcpy(&queue_entry.op, &p_evt->params.flash, sizeof(flash_op_t)); if (fifo_push(&m_flash_fifo, &queue_entry) != NRF_SUCCESS) { __LOG(RTT_CTRL_TEXT_RED "FLASH FIFO FULL :( Increase the fifo size.\n"); return NRF_ERROR_NO_MEM; } NVIC_SetPendingIRQ(FLASH_HANDLER_IRQn); } break; default: return NRF_ERROR_NOT_SUPPORTED; } if (respond) { /* tail recursion */ return bl_cmd_handler(&rsp_cmd); } else { return NRF_SUCCESS; } }