/*****************************************************************************
* 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);
}
Example #3
0
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;
    }
}