This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Internal DFU Hash validation error

Hi,

I am currently working on updating the examples/dfu/secure_bootloader_uart_debug example in SDK 15.3.0 to do an internal dfu on my nrf52840 DK rather than having the nrf communicating to an external source, like my laptop, during the dfu process. In the end, I want an app to store the init and firmware packets into external memory and validate it before going into dfu mode, then transfer the images from external to internal during the dfu process.  I have successfully been able to transfer an init packet from external memory and I have gotten to the execution stage of transferring the firmware image. My current dilemma is when calling the nrf_dfu_validation_hash_ok() I am getting a "Hash verification error" . Originally, I thought this was because the private.key and dfu_public_key.c did not coincide, thus making my dfu package not be able to transfer, however, I soon realized that this was not the case. I have only modified 3 files which you can see below. If anyone has any advice on how I should go about this, please let me know as soon as you can!

 

/**
 * Copyright (c) 2016 - 2019, 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.
 *
 */
#include "nrf_bootloader.h"

#include "compiler_abstraction.h"
#include "nrf.h"
#include "boards.h"
#include "sdk_config.h"
#include "nrf_power.h"
#include "nrf_delay.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_dfu.h"
#include "nrf_error.h"
#include "nrf_dfu_settings.h"
#include "nrf_dfu_utils.h"
#include "nrf_bootloader_wdt.h"
#include "nrf_bootloader_info.h"
#include "nrf_bootloader_app_start.h"
#include "nrf_bootloader_fw_activation.h"
#include "nrf_bootloader_dfu_timers.h"
#include "app_scheduler.h"
#include "nrf_dfu_validation.h"
#include "nrf_drv_qspi.h"

//Opcode for QSPI functions
#define QSPI_STD_CMD_WRSR   0x01
#define QSPI_STD_CMD_RSTEN  0x66
#define QSPI_STD_CMD_RST    0x99
#define QSPI_STD_CMD_WREN   0x06
#define QSPI_STD_CMD_RDSR   0x05
#define QSPI_STD_CMD_RDID   0x9F
#define QSPI_STD_CMD_QREAD   0x6B

//function that waits for the qspi peripheral to finish
#define WAIT_FOR_PERIPH() do { \
        while (!m_finished) {} \
        m_finished = false;    \
    } while (0)

static volatile bool m_finished = false;

static nrf_dfu_observer_t m_user_observer; //<! Observer callback set by the user.
static volatile bool m_flash_write_done;

#define SCHED_QUEUE_SIZE      32          /**< Maximum number of events in the scheduler queue. */
#define SCHED_EVENT_DATA_SIZE NRF_DFU_SCHED_EVENT_DATA_SIZE /**< Maximum app_scheduler event size. */

bool erase_done = false;//flag set when qspi has been erased

#if !(defined(NRF_BL_DFU_ENTER_METHOD_BUTTON)    && \
      defined(NRF_BL_DFU_ENTER_METHOD_PINRESET)  && \
      defined(NRF_BL_DFU_ENTER_METHOD_GPREGRET)  && \
      defined(NRF_BL_DFU_ENTER_METHOD_BUTTONLESS))
    #error Configuration file is missing flags. Update sdk_config.h.
#endif

STATIC_ASSERT((NRF_BL_DFU_INACTIVITY_TIMEOUT_MS >= 100) || (NRF_BL_DFU_INACTIVITY_TIMEOUT_MS == 0),
             "NRF_BL_DFU_INACTIVITY_TIMEOUT_MS must be 100 ms or more, or 0 to indicate that it is disabled.");

#if defined(NRF_LOG_BACKEND_FLASH_START_PAGE)
STATIC_ASSERT(NRF_LOG_BACKEND_FLASH_START_PAGE != 0,
    "If nrf_log flash backend is used it cannot use space after code because it would collide with settings page.");
#endif

/**@brief Weak implemenation of nrf_dfu_init
 *
 * @note   This function will be overridden if nrf_dfu.c is
 *         compiled and linked with the project
 */
 #if (__LINT__ != 1)
__WEAK uint32_t nrf_dfu_init(nrf_dfu_observer_t observer)
{
    NRF_LOG_DEBUG("in weak nrf_dfu_init");
    return NRF_SUCCESS;
}
#endif


/**@brief Weak implementation of nrf_dfu_init
 *
 * @note    This function must be overridden in application if
 *          user-specific initialization is needed.
 */
__WEAK uint32_t nrf_dfu_init_user(void)
{
    NRF_LOG_DEBUG("in weak nrf_dfu_init_user");
    return NRF_SUCCESS;
}


static void flash_write_callback(void * p_context)
{
    UNUSED_PARAMETER(p_context);
    m_flash_write_done = true;
}


static void do_reset(void * p_context)
{
    UNUSED_PARAMETER(p_context);

    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

    NVIC_SystemReset();
}


static void bootloader_reset(bool do_backup)
{
    NRF_LOG_DEBUG("Resetting bootloader.");

    if (do_backup)
    {
        m_flash_write_done = false;
        nrf_dfu_settings_backup(do_reset);
    }
    else
    {
        do_reset(NULL);
    }
}


static void inactivity_timeout(void)
{
    NRF_LOG_INFO("Inactivity timeout.");
    bootloader_reset(true);
}


/**@brief Function for handling DFU events.
 */
static void dfu_observer(nrf_dfu_evt_type_t evt_type)
{
    switch (evt_type)
    {
        case NRF_DFU_EVT_DFU_STARTED:
        case NRF_DFU_EVT_OBJECT_RECEIVED:
            nrf_bootloader_dfu_inactivity_timer_restart(
                        NRF_BOOTLOADER_MS_TO_TICKS(NRF_BL_DFU_INACTIVITY_TIMEOUT_MS),
                        inactivity_timeout);
            break;
        case NRF_DFU_EVT_DFU_COMPLETED:
        case NRF_DFU_EVT_DFU_ABORTED:
            bootloader_reset(true);
            break;
        default:
            break;
    }

    if (m_user_observer)
    {
        m_user_observer(evt_type);
    }
}


/**@brief Function for initializing the event scheduler.
 */
static void scheduler_init(void)
{
    APP_SCHED_INIT(SCHED_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE);
}


/**@brief Suspend the CPU until an interrupt occurs.
 */
static void wait_for_event(void)
{
#if defined(BLE_STACK_SUPPORT_REQD) || defined(ANT_STACK_SUPPORT_REQD)
    (void)sd_app_evt_wait();
#else
    // Wait for an event.
    __WFE();
    // Clear the internal event register.
    __SEV();
    __WFE();
#endif
}


/**@brief Continually sleep and process tasks whenever woken.
 */
static void loop_forever(void)
{
    while (true)
    {
        //feed the watchdog if enabled.
        nrf_bootloader_wdt_feed();

        app_sched_execute();

        if (!NRF_LOG_PROCESS())
        {
            wait_for_event();
        }
    }
}

/**@brief Function for initializing button used to enter DFU mode.
 */
static void dfu_enter_button_init(void)
{
    nrf_gpio_cfg_sense_input(NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN,
                             BUTTON_PULL,
                             NRF_GPIO_PIN_SENSE_LOW);
}


static bool crc_on_valid_app_required(void)
{
    bool ret = true;
    if (NRF_BL_APP_CRC_CHECK_SKIPPED_ON_SYSTEMOFF_RESET &&
        (nrf_power_resetreas_get() & NRF_POWER_RESETREAS_OFF_MASK))
    {
        nrf_power_resetreas_clear(NRF_POWER_RESETREAS_OFF_MASK);
        ret = false;
    }
    else if (NRF_BL_APP_CRC_CHECK_SKIPPED_ON_GPREGRET2 &&
            ((nrf_power_gpregret2_get() & BOOTLOADER_DFU_GPREGRET2_MASK) == BOOTLOADER_DFU_GPREGRET2)
            && (nrf_power_gpregret2_get() & BOOTLOADER_DFU_SKIP_CRC_BIT_MASK))
    {
        nrf_power_gpregret2_set(nrf_power_gpregret2_get() & ~BOOTLOADER_DFU_SKIP_CRC);
        ret = false;
    }
    else
    {
    }

    return ret;
}



static bool boot_validate(boot_validation_t const * p_validation, uint32_t data_addr, uint32_t data_len, bool do_crc)
{
    if (!do_crc && (p_validation->type == VALIDATE_CRC))
    {
        return true;
    }
    return nrf_dfu_validation_boot_validate(p_validation, data_addr, data_len);
}


/** @brief Function for checking if the main application is valid.
 *
 * @details     This function checks if there is a valid application
 *              located at Bank 0.
 *
 * @param[in]   do_crc Perform CRC check on application. Only CRC checks
                       can be skipped. For other boot validation types,
                       this parameter is ignored.
 *
 * @retval  true  If a valid application has been detected.
 * @retval  false If there is no valid application.
 */
static bool app_is_valid(bool do_crc)
{
    if (s_dfu_settings.bank_0.bank_code != NRF_DFU_BANK_VALID_APP)
    {
        NRF_LOG_INFO("Boot validation failed. No valid app to boot.");
        return false;
    }
    else if (NRF_BL_APP_SIGNATURE_CHECK_REQUIRED &&
        (s_dfu_settings.boot_validation_app.type != VALIDATE_ECDSA_P256_SHA256))
    {
        NRF_LOG_WARNING("Boot validation failed. The boot validation of the app must be a signature check.");
        return false;
    }
    else if (SD_PRESENT && !boot_validate(&s_dfu_settings.boot_validation_softdevice, MBR_SIZE, s_dfu_settings.sd_size, do_crc))
    {
        NRF_LOG_WARNING("Boot validation failed. SoftDevice is present but invalid.");
        return false;
    }
    else if (!boot_validate(&s_dfu_settings.boot_validation_app, nrf_dfu_bank0_start_addr(), s_dfu_settings.bank_0.image_size, do_crc))
    {
        NRF_LOG_WARNING("Boot validation failed. App is invalid.");
        return false;
    }
    // The bootloader itself is not checked, since a self-check of this kind gives little to no benefit
    // compared to the cost incurred on each bootup.

    NRF_LOG_DEBUG("App is valid");
    return true;
}



/**@brief Function for clearing all DFU enter flags that
 *        preserve state during reset.
 *
 * @details This is used to make sure that each of these flags
 *          is checked only once after reset.
 */
static void dfu_enter_flags_clear(void)
{
    if (NRF_BL_DFU_ENTER_METHOD_PINRESET &&
       (NRF_POWER->RESETREAS & POWER_RESETREAS_RESETPIN_Msk))
    {
        // Clear RESETPIN flag.
        NRF_POWER->RESETREAS |= POWER_RESETREAS_RESETPIN_Msk;
    }

    if (NRF_BL_DFU_ENTER_METHOD_GPREGRET &&
       ((nrf_power_gpregret_get() & BOOTLOADER_DFU_GPREGRET_MASK) == BOOTLOADER_DFU_GPREGRET)
            && (nrf_power_gpregret_get() & BOOTLOADER_DFU_START_BIT_MASK))
    {
        // Clear DFU mark in GPREGRET register.
        nrf_power_gpregret_set(nrf_power_gpregret_get() & ~BOOTLOADER_DFU_START);
    }

    if (NRF_BL_DFU_ENTER_METHOD_BUTTONLESS &&
       (s_dfu_settings.enter_buttonless_dfu == 1))
    {
        // Clear DFU flag in flash settings.
        s_dfu_settings.enter_buttonless_dfu = 0;
        APP_ERROR_CHECK(nrf_dfu_settings_write(NULL));
    }
}


/**@brief Function for checking whether to enter DFU mode or not.
 */
static bool dfu_enter_check(void)
{
    if (!app_is_valid(crc_on_valid_app_required()))
    {
        NRF_LOG_DEBUG("DFU mode because app is not valid.");
        return true;
    }

    if (NRF_BL_DFU_ENTER_METHOD_BUTTON &&
       (nrf_gpio_pin_read(NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN) == 0))
    {
        NRF_LOG_DEBUG("DFU mode requested via button.");
        return true;
    }

    if (NRF_BL_DFU_ENTER_METHOD_PINRESET &&
       (NRF_POWER->RESETREAS & POWER_RESETREAS_RESETPIN_Msk))
    {
        NRF_LOG_DEBUG("DFU mode requested via pin-reset.");
        return true;
    }

    if (NRF_BL_DFU_ENTER_METHOD_GPREGRET &&
       (nrf_power_gpregret_get() & BOOTLOADER_DFU_START))
    {
        NRF_LOG_DEBUG("DFU mode requested via GPREGRET.");
        return true;
    }

    if (NRF_BL_DFU_ENTER_METHOD_BUTTONLESS &&
       (s_dfu_settings.enter_buttonless_dfu == 1))
    {
        NRF_LOG_DEBUG("DFU mode requested via bootloader settings.");
        return true;
    }

    return false;
}


#if NRF_BL_DFU_ALLOW_UPDATE_FROM_APP
static void postvalidate(void)
{
    NRF_LOG_INFO("Postvalidating update after reset.");
    nrf_dfu_validation_init();

    if (nrf_dfu_validation_init_cmd_present())
    {
        uint32_t firmware_start_addr;
        uint32_t firmware_size;

        // Execute a previously received init packed. Subsequent executes will have no effect.
        if (nrf_dfu_validation_init_cmd_execute(&firmware_start_addr, &firmware_size) == NRF_DFU_RES_CODE_SUCCESS)
        {
            if (nrf_dfu_validation_prevalidate() == NRF_DFU_RES_CODE_SUCCESS)
            {
                if (nrf_dfu_validation_activation_prepare(firmware_start_addr, firmware_size) == NRF_DFU_RES_CODE_SUCCESS)
                {
                    NRF_LOG_INFO("Postvalidation successful.");
                }
            }
        }
    }

    s_dfu_settings.bank_current = NRF_DFU_CURRENT_BANK_0;
    UNUSED_RETURN_VALUE(nrf_dfu_settings_write_and_backup(flash_write_callback));
}
#endif
static void qspi_handler(nrf_drv_qspi_evt_t event, void * p_context)
{
    UNUSED_PARAMETER(event);
    UNUSED_PARAMETER(p_context);
    m_finished = true;
}

static void configure_memory()
{
    uint8_t temporary = 0x40;//qspi enable code, this if from NOR flash datasheet
    uint32_t err_code;
    uint8_t rx[4];
    nrf_qspi_cinstr_conf_t cinstr_cfg = {
        .opcode    = QSPI_STD_CMD_RSTEN,
        .length    = NRF_QSPI_CINSTR_LEN_1B,
        .io2_level = true,
        .io3_level = true,
        .wipwait   = true,
        .wren      = true
    };

    // Send reset enable
    err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
    APP_ERROR_CHECK(err_code);

    // Send reset command
    cinstr_cfg.opcode = QSPI_STD_CMD_RST;
    err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
    APP_ERROR_CHECK(err_code);

    // Send write enable
    cinstr_cfg.opcode = QSPI_STD_CMD_WREN;
    err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
    APP_ERROR_CHECK(err_code);

    // Switch to qspi mode
    cinstr_cfg.opcode = QSPI_STD_CMD_WRSR;
    cinstr_cfg.length = NRF_QSPI_CINSTR_LEN_2B;
    err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, &temporary, NULL);
    APP_ERROR_CHECK(err_code);

    // Read ID
    cinstr_cfg.opcode = QSPI_STD_CMD_RDID;
    cinstr_cfg.length = NRF_QSPI_CINSTR_LEN_4B;
    err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, NULL, &rx);
    APP_ERROR_CHECK(err_code);
}

ret_code_t nrf_bootloader_init(nrf_dfu_observer_t observer)
{
    NRF_LOG_DEBUG("In nrf_bootloader_init");

    ret_code_t                            ret_val;
    nrf_bootloader_fw_activation_result_t activation_result;
    uint32_t                              initial_timeout;
    bool                                  dfu_enter = false;

    m_user_observer = observer;

    if (NRF_BL_DFU_ENTER_METHOD_BUTTON)
    {
        dfu_enter_button_init();
    }

    ret_val = nrf_dfu_settings_init(false);
    if (ret_val != NRF_SUCCESS)
    {
        return NRF_ERROR_INTERNAL;
    }

    #if NRF_BL_DFU_ALLOW_UPDATE_FROM_APP
    // Postvalidate if DFU has signaled that update is ready.
    if (s_dfu_settings.bank_current == NRF_DFU_CURRENT_BANK_1)
    {
        postvalidate();
    }
    #endif

    // Check if an update needs to be activated and activate it.
    activation_result = nrf_bootloader_fw_activate();

    switch (activation_result)
    {
        case ACTIVATION_NONE:
            initial_timeout = NRF_BOOTLOADER_MS_TO_TICKS(NRF_BL_DFU_INACTIVITY_TIMEOUT_MS);
            dfu_enter       = dfu_enter_check();
            break;

        case ACTIVATION_SUCCESS_EXPECT_ADDITIONAL_UPDATE:
            initial_timeout = NRF_BOOTLOADER_MS_TO_TICKS(NRF_BL_DFU_CONTINUATION_TIMEOUT_MS);
            dfu_enter       = true;
            break;

        case ACTIVATION_SUCCESS:
            bootloader_reset(true);
            NRF_LOG_ERROR("Unreachable");
            return NRF_ERROR_INTERNAL; // Should not reach this.

        case ACTIVATION_ERROR:
        default:
            return NRF_ERROR_INTERNAL;
    }

    if (dfu_enter)
    {
        nrf_bootloader_wdt_init();
        scheduler_init();
        dfu_enter_flags_clear();

        //If QSPI not already enabled/initialized
        if((NRF_QSPI->STATUS >> 24)==0x00)//is status register QSPI bit enabled?
        { // if not, initialize qspi
          nrf_drv_qspi_config_t config = NRF_DRV_QSPI_DEFAULT_CONFIG;
          ret_val = nrf_drv_qspi_init(&config, qspi_handler, NULL);

          if(ret_val!=NRF_SUCCESS)//when debugging if initialization not working, reset and reinitialize
          {
            NVIC_SystemReset(); //if not initialized correctly, reset
          }

          configure_memory(); //configure the qspi external memory

          //if the external memory already has init and firmware packages, delete this
          //if(erase_done == false)//check if chip has already been erased - case when dfu downloaded from cell module
          //{
          //  //erase chip erase
          //  m_finished = false;
          //  ret_code_t ret_val = nrf_drv_qspi_erase(NRF_QSPI_ERASE_LEN_64KB, 0);//erase 64KBytes or memory starting at address 0x00
          //  APP_ERROR_CHECK(ret_val);
          //  WAIT_FOR_PERIPH();
          //  erase_done = true;
          //}
        }
        // Call user-defined init function if implemented
        //not used
        ret_val = nrf_dfu_init_user();
        if (ret_val != NRF_SUCCESS)
        {
            return NRF_ERROR_INTERNAL;
        }

        //Currently disabled due to SD dfu update time taking too long
        nrf_bootloader_dfu_inactivity_timer_restart(initial_timeout, inactivity_timeout);//restart inactivity timer

        ret_val = nrf_dfu_init(dfu_observer);//bootloader goes into dfu
        if (ret_val != NRF_SUCCESS)
        {
            return NRF_ERROR_INTERNAL;
        }

        NRF_LOG_DEBUG("Enter main loop");
        loop_forever(); // This function will never return.
        NRF_LOG_ERROR("Unreachable");
    }
    else
    {
        // Erase additional data like peer data or advertisement name
        ret_val = nrf_dfu_settings_additional_erase();
        if (ret_val != NRF_SUCCESS)
        {
            return NRF_ERROR_INTERNAL;
        }
       
        //if qspi initialized uninitialize the qspi
        while((NRF_QSPI->STATUS >> 24)==0x01)
        {
          nrf_drv_qspi_uninit();
        }

        m_flash_write_done = false;
        nrf_dfu_settings_backup(flash_write_callback);
        ASSERT(m_flash_write_done);

        nrf_bootloader_app_start();
        NRF_LOG_ERROR("Unreachable");
    }

    // Should not be reached.
    return NRF_ERROR_INTERNAL;
}
nrf_bootloader.h
/**
 * Copyright (c) 2016 - 2019, 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.
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nrf_dfu.h"

#include "nrf_dfu_utils.h"
#include "nrf_dfu_transport.h"
#include "nrf_dfu_req_handler.h"
#include "nrf_log.h"
#include "nrf_drv_qspi.h"
#include "nrf_dfu_serial.h"
#include "nrf_delay.h"
#include "nrf_dfu_flash.h"
#include "nrf_dfu_validation.h"
#include "nrf_dfu_settings.h"

//*********** Location of the images ***********
#define APPLICATION_INIT_PACKET_LOCATION 0x0000
#define APPLICATION_INIT_PACKET_MAX_SIZE 0x0200
#define APPLICATION_IMAGE_LOCATION 0x1000
#define APPLICATION_MAX_SIZE 0x20000
#define BOOTLOADER_SOFTDEVICE_INIT_PACKET_LOCATION 0x20000
#define BOOTLOADER_SOFDEVICE_IMAGE_LOCATION 0x21000
#define BOOTLOADER_SOFTDEVICE_MAX_SIZE 0x40000
//*******************************************
#define COMMAND_OBJECT              0x01 //For init packet 
#define DATA_OBJECT                 0x02 // For actual image packet
//*******************************************

uint16_t mtu_size = 200;
static nrf_dfu_observer_t m_user_observer;                          //<! Observer callback set by the user.
static uint32_t m_firmware_start_addr;          /**< Start address of the current firmware image. */
static uint32_t m_firmware_size_req;            /**< The size of the entire firmware image. Defined by the init command. */
/**
 * @brief This function calls the user's observer (@ref m_observer) after it is done handling the event.
 */
static void dfu_observer(nrf_dfu_evt_type_t event)
{
    switch (event)
    {
        case NRF_DFU_EVT_DFU_COMPLETED:
        {
#ifndef NRF_DFU_NO_TRANSPORT
            UNUSED_RETURN_VALUE(nrf_dfu_transports_close(NULL));
#endif
            break;
        }
        default:
            break;
    }

    /* Call user's observer if present. */
    if (m_user_observer)
    {
        m_user_observer(event);
    }
}

// Simple algorithm for finding the end of the firmware image in the external memory
uint32_t find_image_size(uint32_t start_address, size_t length)
{
    uint32_t img_size = 0;//start the image size at 0
    uint8_t m_rx_buf[length];
    nrf_drv_qspi_read(&m_rx_buf,length,start_address);//load init packet from flash into buffer
    nrf_delay_ms(50);
    while(img_size < length)
    {
        if(m_rx_buf[img_size] != 0xFF || m_rx_buf[img_size+1]!=0xFF || m_rx_buf[img_size+2]!=0xFF)
          //if the current byte and the next byte, are not cleared, go to the next byte
            img_size += 1;// keep count of the image size
        else
            break;//if the current and the next byte are cleared ( == 0xFF) then break and return image size
    }  
    
    return img_size;    
}

void init_pkt_transfer(void)
{
    nrf_dfu_response_t p_res;
    nrf_dfu_request_t p_req;

    uint32_t len = find_image_size(APPLICATION_INIT_PACKET_LOCATION,
                                               APPLICATION_INIT_PACKET_MAX_SIZE);
    uint8_t m_rx_buf[len];
    //Get MTU
    p_req.request = NRF_DFU_OP_MTU_GET;
    p_res.mtu.size = mtu_size;
    on_mtu_get_request(&p_req,&p_res);
    
    //select command
    p_req.request           = NRF_DFU_OP_OBJECT_SELECT;
    p_req.callback.response = dfu_req_handler_rsp_clbk;    
    p_req.select.object_type = COMMAND_OBJECT;
    nrf_dfu_command_req(&p_req,&p_res);
    
    //Create command
    p_req.request = NRF_DFU_OP_OBJECT_CREATE;
    p_req.create.object_type = COMMAND_OBJECT;
    p_req.create.object_size = len;
    nrf_dfu_command_req(&p_req,&p_res);

    //Write Command - writing from external to internal memory
    p_req.request = NRF_DFU_OP_OBJECT_WRITE;//change the opcode to write 
    nrf_drv_qspi_read(m_rx_buf,len+1,APPLICATION_INIT_PACKET_LOCATION);//read init packet from external memory into buffer starting at init packet location
    nrf_delay_ms(5);
    p_req.write.p_data = m_rx_buf;
    nrf_delay_ms(5);
    p_req.write.len = len;
    nrf_dfu_command_req(&p_req,&p_res);

    free((void*)p_req.write.p_data);

    //Calculate CRC Command
    p_req.request           = NRF_DFU_OP_CRC_GET; 
    nrf_dfu_command_req(&p_req,&p_res);

    //Execut Init packet Command
    p_req.request           = NRF_DFU_OP_OBJECT_EXECUTE; 
    nrf_dfu_command_req(&p_req,&p_res);

}

void fw_pkt_transfer(void)
{
    nrf_dfu_response_t p_res;
    nrf_dfu_request_t p_req;

    uint32_t len = find_image_size(APPLICATION_IMAGE_LOCATION, APPLICATION_IMAGE_LOCATION + APPLICATION_MAX_SIZE);

    uint8_t m_rx_buf[mtu_size];
    //select command
    p_req.request           = NRF_DFU_OP_OBJECT_SELECT;
    p_req.callback.response = dfu_req_handler_rsp_clbk;    
    p_req.select.object_type = DATA_OBJECT;
    nrf_dfu_data_req(&p_req,&p_res);
    
    //Create command
    p_req.request = NRF_DFU_OP_OBJECT_CREATE;
    p_req.create.object_type = DATA_OBJECT;
    p_req.create.object_size = len;
    nrf_dfu_data_req(&p_req,&p_res);

    //Write Command - writing from external to internal memory 
    uint32_t write_addr = APPLICATION_IMAGE_LOCATION;
    p_req.request = NRF_DFU_OP_OBJECT_WRITE;//change the opcode to write 
    while(s_dfu_settings.write_offset!=len)
    {
        memset(m_rx_buf,0,sizeof(m_rx_buf));//clear buffer
        if((s_dfu_settings.write_offset + mtu_size) < len)//if the current image size is less that the required image size
        {
            nrf_drv_qspi_read(m_rx_buf, mtu_size ,write_addr + s_dfu_settings.write_offset);//read init packet from external memory into buffer starting at init packet location
            nrf_delay_ms(5);//flush delay
            p_req.write.p_data = m_rx_buf;
            nrf_delay_ms(5);//flush delay
            p_req.write.len = mtu_size;
        }
        else
        {
            nrf_drv_qspi_read(m_rx_buf, len - s_dfu_settings.write_offset ,write_addr + s_dfu_settings.write_offset);//read init packet from external memory into buffer starting at init packet location
            nrf_delay_ms(5);
            p_req.write.p_data = m_rx_buf;
            nrf_delay_ms(5);
            p_req.write.len = len-s_dfu_settings.write_offset;
        }
        nrf_dfu_data_req(&p_req,&p_res);
        //printf("Next s_dfu_settings.write_offset size: %i\n",s_dfu_settings.write_offset);
        free((void*)p_req.write.p_data);
    }  
    //Calculate CRC Command
    p_req.request           = NRF_DFU_OP_CRC_GET; 
    nrf_dfu_data_req(&p_req,&p_res);

    //Execut Init packet Command
    p_req.request           = NRF_DFU_OP_OBJECT_EXECUTE; 
    nrf_dfu_data_req(&p_req,&p_res);
}

uint32_t nrf_dfu_init(nrf_dfu_observer_t observer)
{
    uint32_t ret_val;

    m_user_observer = observer;

    NRF_LOG_INFO("Entering DFU mode.");

    dfu_observer(NRF_DFU_EVT_DFU_INITIALIZED);
    ret_val = nrf_dfu_flash_init(false);

    if (ret_val != NRF_SUCCESS)
    {
        return ret_val;
    }

    nrf_dfu_validation_init();

    init_pkt_transfer();

    fw_pkt_transfer();

    return ret_val;
}
nrf_dfu.h
/**
 * Copyright (c) 2016 - 2019, 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.
 *
 */
#include <stdint.h>
#include <stdbool.h>
#include "sdk_config.h"
#include "nrf_dfu.h"
#include "nrf_dfu_types.h"
#include "nrf_dfu_req_handler.h"
#include "nrf_dfu_handling_error.h"
#include "nrf_dfu_settings.h"
#include "nrf_dfu_utils.h"
#include "nrf_dfu_flash.h"
#include "nrf_fstorage.h"
#include "nrf_bootloader_info.h"
#include "app_util.h"
#include "pb.h"
#include "pb_common.h"
#include "pb_decode.h"
#include "dfu-cc.pb.h"
#include "crc32.h"
#include "app_scheduler.h"
#include "sdk_macros.h"
#include "nrf_crypto.h"
#include "nrf_assert.h"
#include "nrf_dfu_validation.h"
#include "nrf_drv_qspi.h"
#include "nrf_delay.h"

#define NRF_LOG_MODULE_NAME nrf_dfu_req_handler
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();

#define NRF_DFU_PROTOCOL_VERSION    (0x01)

#define QSPI_XIP_START_ADDR         0x12000000

#ifndef NRF_DFU_PROTOCOL_REDUCED
#define NRF_DFU_PROTOCOL_REDUCED 0
#endif

STATIC_ASSERT(DFU_SIGNED_COMMAND_SIZE <= INIT_COMMAND_MAX_SIZE);

#define QSPI_STD_CMD_WRSR   0x01
#define QSPI_STD_CMD_RSTEN  0x66
#define QSPI_STD_CMD_RST    0x99
#define QSPI_STD_CMD_WREN   0x06
#define QSPI_STD_CMD_RDSR   0x05
#define QSPI_STD_CMD_RDID   0x9F
#define QSPI_STD_CMD_QREAD   0x6B

#define WAIT_FOR_PERIPH() do { \
        while (!m_finished) {} \
        m_finished = false;    \
    } while (0)

static volatile bool m_finished = false;

static uint32_t m_firmware_start_addr;          /**< Start address of the current firmware image. */
static uint32_t m_firmware_size_req;            /**< The size of the entire firmware image. Defined by the init command. */

static nrf_dfu_observer_t m_observer;

static uint8_t m_rx_buff[64];

static void qspi_handler(nrf_drv_qspi_evt_t event, void * p_context)
{
    UNUSED_PARAMETER(event);
    UNUSED_PARAMETER(p_context);
    m_finished = true;
}

static void on_dfu_complete(nrf_fstorage_evt_t * p_evt)
{
    UNUSED_PARAMETER(p_evt);

    NRF_LOG_DEBUG("All flash operations have completed. DFU completed.");

    m_observer(NRF_DFU_EVT_DFU_COMPLETED);
}


static nrf_dfu_result_t ext_err_code_handle(nrf_dfu_result_t ret_val)
{
    if (ret_val < NRF_DFU_RES_CODE_EXT_ERROR)
    {
        return ret_val;
    }
    else
    {
        nrf_dfu_ext_error_code_t ext_err =
                (nrf_dfu_ext_error_code_t)((uint8_t)ret_val - (uint8_t)NRF_DFU_RES_CODE_EXT_ERROR);
        return ext_error_set(ext_err);
    }
}


#if !NRF_DFU_PROTOCOL_REDUCED
static void on_protocol_version_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
{
    UNUSED_PARAMETER(p_req);
    NRF_LOG_DEBUG("Handle NRF_DFU_OP_PROTOCOL_VERSION");

    if (NRF_DFU_PROTOCOL_VERSION_MSG)
    {
        p_res->protocol.version = NRF_DFU_PROTOCOL_VERSION;
    }
    else
    {
        NRF_LOG_DEBUG("NRF_DFU_OP_PROTOCOL_VERSION disabled.");
        p_res->result = NRF_DFU_RES_CODE_OP_CODE_NOT_SUPPORTED;
    }
}


static void on_hw_version_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
{
    NRF_LOG_DEBUG("Handle NRF_DFU_OP_HARDWARE_VERSION");

    p_res->hardware.part    = NRF_FICR->INFO.PART;
    p_res->hardware.variant = NRF_FICR->INFO.VARIANT;

    /* FICR values are in Kilobytes, we report them in bytes. */
    p_res->hardware.memory.ram_size      = NRF_FICR->INFO.RAM   * 1024;
    p_res->hardware.memory.rom_size      = NRF_FICR->INFO.FLASH * 1024;
    p_res->hardware.memory.rom_page_size = NRF_FICR->CODEPAGESIZE;
}


static void on_fw_version_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
{
    NRF_LOG_DEBUG("Handle NRF_DFU_OP_FIRMWARE_VERSION");
    NRF_LOG_DEBUG("Firmware image requested: %d", p_req->firmware.image_number);

    if (NRF_DFU_PROTOCOL_FW_VERSION_MSG)
    {
        uint8_t fw_count = 1;

        if (SD_PRESENT)
        {
            fw_count++;
        }

        if (s_dfu_settings.bank_0.bank_code == NRF_DFU_BANK_VALID_APP)
        {
            fw_count++;
        }

        p_res->result = NRF_DFU_RES_CODE_SUCCESS;

        if (p_req->firmware.image_number == 0)
        {
            /* Bootloader is always present and it is always image zero. */
            p_res->firmware.type    = NRF_DFU_FIRMWARE_TYPE_BOOTLOADER;
            p_res->firmware.version = s_dfu_settings.bootloader_version;
            p_res->firmware.addr    = BOOTLOADER_START_ADDR;
            p_res->firmware.len     = BOOTLOADER_SIZE;
        }
        else if ((p_req->firmware.image_number == 1) && SD_PRESENT)
        {
            /* If a SoftDevice is present, it will be firmware image one. */
            p_res->firmware.type    = NRF_DFU_FIRMWARE_TYPE_SOFTDEVICE;
            p_res->firmware.version = SD_VERSION_GET(MBR_SIZE);
            p_res->firmware.addr    = MBR_SIZE;
            p_res->firmware.len     = SD_SIZE_GET(MBR_SIZE);
        }
        else if ((p_req->firmware.image_number < fw_count))
        {
            /* Either there is no SoftDevice and the firmware image requested is one,
             * or there is a SoftDevice and the firmware image requested is two.
             */
            p_res->firmware.type    = NRF_DFU_FIRMWARE_TYPE_APPLICATION;
            p_res->firmware.version = s_dfu_settings.app_version;
            p_res->firmware.addr    = nrf_dfu_app_start_address();
            p_res->firmware.len     = s_dfu_settings.bank_0.image_size;
        }
        else
        {
            NRF_LOG_DEBUG("No such firmware image");
            p_res->firmware.type    = NRF_DFU_FIRMWARE_TYPE_UNKNOWN;
            p_res->firmware.version = 0x00;
            p_res->firmware.addr    = 0x00;
            p_res->firmware.len     = 0x00;
        }
    }
    else
    {
        NRF_LOG_DEBUG("NRF_DFU_OP_FIRMWARE_VERSION disabled.");
        p_res->result        = NRF_DFU_RES_CODE_OP_CODE_NOT_SUPPORTED;
        p_res->firmware.type = NRF_DFU_FIRMWARE_TYPE_UNKNOWN;
    }
}


static void on_ping_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
{
    NRF_LOG_DEBUG("Handle NRF_DFU_OP_PING");
    p_res->ping.id = p_req->ping.id;
}


void on_mtu_get_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
{
    NRF_LOG_DEBUG("Handle NRF_DFU_OP_MTU_GET");
    p_res->mtu.size = p_req->mtu.size;
}
#endif // !NRF_DFU_PROTOCOL_REDUCED


static void on_prn_set_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
{
    UNUSED_PARAMETER(p_req);
    UNUSED_PARAMETER(p_res);
    NRF_LOG_DEBUG("Handle NRF_DFU_OP_RECEIPT_NOTIF_SET");
}


static void on_abort_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
{
    UNUSED_PARAMETER(p_req);
    UNUSED_PARAMETER(p_res);
    NRF_LOG_DEBUG("Handle NRF_DFU_OP_ABORT");

    m_observer(NRF_DFU_EVT_DFU_ABORTED);
}


/* Set offset and CRC fields in the response for a 'command' message. */
static void cmd_response_offset_and_crc_set(nrf_dfu_response_t * const p_res)
{
    ASSERT(p_res);

    /* Copy the CRC and offset of the init packet. */
    p_res->crc.offset = s_dfu_settings.progress.command_offset;
    p_res->crc.crc    = s_dfu_settings.progress.command_crc;
}


static void on_cmd_obj_select_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
{
    UNUSED_PARAMETER(p_req);
    NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_SELECT (command)");

    p_res->select.max_size = INIT_COMMAND_MAX_SIZE;
    cmd_response_offset_and_crc_set(p_res);
}


static void on_cmd_obj_create_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
{
    ASSERT(p_req);
    ASSERT(p_res);

    NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_CREATE (command)");

    //m_observer(NRF_DFU_EVT_DFU_STARTED);

    nrf_dfu_result_t ret_val = nrf_dfu_validation_init_cmd_create(p_req->create.object_size);
    p_res->result = ext_err_code_handle(ret_val);
}


static void on_cmd_obj_write_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
{
    ASSERT(p_req);
    ASSERT(p_req->write.p_data);
    ASSERT(p_req->write.len);
    ASSERT(p_res);

    NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_WRITE (command)");

    nrf_dfu_result_t ret_val;

    ret_val = nrf_dfu_validation_init_cmd_append(p_req->write.p_data, p_req->write.len);
    p_res->result = ext_err_code_handle(ret_val);

    /* Update response. This is only used when the PRN is triggered and the 'write' message
     * is answered with a CRC message and these field are copied into the response. */
    cmd_response_offset_and_crc_set(p_res);

    /* If a callback to free the request payload buffer was provided, invoke it now. */
    //if (p_req->callback.write)
    //{
    //    p_req->callback.write((void*)p_req->write.p_data);
    //}
}


static void on_cmd_obj_execute_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
{
    ASSERT(p_req);
    ASSERT(p_res);

    NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_EXECUTE (command)");

    nrf_dfu_result_t ret_val;
    ret_val = nrf_dfu_validation_init_cmd_execute(&m_firmware_start_addr, &m_firmware_size_req);
    p_res->result = ext_err_code_handle(ret_val);

    if (p_res->result == NRF_DFU_RES_CODE_SUCCESS)
    {
        if (nrf_dfu_settings_write_and_backup(NULL) == NRF_SUCCESS)
        {
            /* Setting DFU to initialized */
            NRF_LOG_DEBUG("Writing valid init command to flash.");
        }
        else
        {
            p_res->result = NRF_DFU_RES_CODE_OPERATION_FAILED;
        }
    }
}


static void on_cmd_obj_crc_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
{
    UNUSED_PARAMETER(p_req);
    NRF_LOG_DEBUG("Handle NRF_DFU_OP_CRC_GET (command)");

    cmd_response_offset_and_crc_set(p_res);
}


/** @brief Function handling command requests from the transport layer.
 *
 * @param   p_req[in]       Pointer to the structure holding the DFU request.
 * @param   p_res[out]      Pointer to the structure holding the DFU response.
 *
 * @retval NRF_SUCCESS      If the command request was executed successfully.
 *                          Any other error code indicates that the data request
 *                          could not be handled.
 */
void nrf_dfu_command_req(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
{
    ASSERT(p_req);
    ASSERT(p_res);

    switch (p_req->request)
    {
        case NRF_DFU_OP_OBJECT_CREATE:
        {
            on_cmd_obj_create_request(p_req, p_res);
        } break;

        case NRF_DFU_OP_CRC_GET:
        {
            on_cmd_obj_crc_request(p_req, p_res);
        } break;

        case NRF_DFU_OP_OBJECT_WRITE:
        {
            on_cmd_obj_write_request(p_req, p_res);
        } break;

        case NRF_DFU_OP_OBJECT_EXECUTE:
        {
            on_cmd_obj_execute_request(p_req, p_res);
        } break;

        case NRF_DFU_OP_OBJECT_SELECT:
        {
            on_cmd_obj_select_request(p_req, p_res);
        } break;

        default:
        {
            ASSERT(false);
        } break;
    }
}


static void on_data_obj_select_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
{
    NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_SELECT (data)");

    p_res->select.crc    = s_dfu_settings.progress.firmware_image_crc;
    p_res->select.offset = s_dfu_settings.progress.firmware_image_offset;

    p_res->select.max_size = DATA_OBJECT_MAX_SIZE;

    NRF_LOG_DEBUG("crc = 0x%x, offset = 0x%x, max_size = 0x%x",
                  p_res->select.crc,
                  p_res->select.offset,
                  p_res->select.max_size);
}


static void on_data_obj_create_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
{
    NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_CREATE (data)");

    if (!nrf_dfu_validation_init_cmd_present())
    {
        /* Can't accept data because DFU isn't initialized by init command. */
        NRF_LOG_ERROR("Cannot create data object without valid init command");
        p_res->result = NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED;
        return;
    }

    if (p_req->create.object_size == 0)
    {
        NRF_LOG_ERROR("Object size cannot be 0.")
        p_res->result = NRF_DFU_RES_CODE_INVALID_PARAMETER;
        return;
    }

    if (  ((p_req->create.object_size & (CODE_PAGE_SIZE - 1)) != 0)
        && (s_dfu_settings.progress.firmware_image_offset_last + p_req->create.object_size != m_firmware_size_req))
    {
        NRF_LOG_ERROR("Object size must be page aligned");
        p_res->result = NRF_DFU_RES_CODE_INVALID_PARAMETER;
        return;
    }

    if (p_req->create.object_size > DATA_OBJECT_MAX_SIZE)
    {
        /* It is impossible to handle the command because the size is too large */
        NRF_LOG_ERROR("Invalid size for object (too large)");
        p_res->result = NRF_DFU_RES_CODE_INSUFFICIENT_RESOURCES;
        return;
    }

    if ((s_dfu_settings.progress.firmware_image_offset_last + p_req->create.object_size) >
        m_firmware_size_req)
    {
        NRF_LOG_ERROR("Creating the object with size 0x%08x would overflow firmware size. "
                      "Offset is 0x%08x and firmware size is 0x%08x.",
                      p_req->create.object_size,
                      s_dfu_settings.progress.firmware_image_offset_last,
                      m_firmware_size_req);

        p_res->result = NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED;
        return;
    }

    s_dfu_settings.progress.data_object_size      = p_req->create.object_size;
    s_dfu_settings.progress.firmware_image_crc    = s_dfu_settings.progress.firmware_image_crc_last;
    s_dfu_settings.progress.firmware_image_offset = s_dfu_settings.progress.firmware_image_offset_last;
    s_dfu_settings.write_offset                   = s_dfu_settings.progress.firmware_image_offset_last;

    /* Erase the page we're at. 
    if (nrf_dfu_flash_erase((m_firmware_start_addr + s_dfu_settings.progress.firmware_image_offset),
                            CEIL_DIV(p_req->create.object_size, CODE_PAGE_SIZE), NULL) != NRF_SUCCESS)
    if(nrf_drv_qspi_chip_erase() != NRFX_SUCCESS)
    {
        NRF_LOG_ERROR("Erase operation failed");
        p_res->result = NRF_DFU_RES_CODE_INVALID_OBJECT;
        return;
    }*/

    NRF_LOG_DEBUG("Creating object with size: %d. Offset: 0x%08x, CRC: 0x%08x",
                 s_dfu_settings.progress.data_object_size,
                 s_dfu_settings.progress.firmware_image_offset,
                 s_dfu_settings.progress.firmware_image_crc);
  
}


static void on_data_obj_write_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
{
    NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_WRITE (data)");

    if (!nrf_dfu_validation_init_cmd_present())
    {
        /* Can't accept data because DFU isn't initialized by init command. */
        p_res->result = NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED;
        return;
    }

    uint32_t const data_object_offset = s_dfu_settings.progress.firmware_image_offset -
                                        s_dfu_settings.progress.firmware_image_offset_last;

    if ((p_req->write.len + data_object_offset) > s_dfu_settings.progress.data_object_size)
    {
        /* Can't accept data because too much data has been received. */
        NRF_LOG_ERROR("Write request too long");
        p_res->result = NRF_DFU_RES_CODE_INVALID_PARAMETER;
        return;
    }

    uint32_t const write_addr = m_firmware_start_addr + s_dfu_settings.write_offset;
    /* CRC must be calculated before handing off the data to fstorage because the data is
     * freed on write completion.
     */
    uint32_t const next_crc =
        crc32_compute(p_req->write.p_data, p_req->write.len, &s_dfu_settings.progress.firmware_image_crc);

    ASSERT(p_req->callback.write);

    ret_code_t ret =
        nrf_dfu_flash_store(write_addr, p_req->write.p_data, p_req->write.len, NULL);

    if (ret != NRF_SUCCESS)
    {
        /* When nrf_dfu_flash_store() fails because there is no space in the queue,
         * stop processing the request so that the peer can detect a CRC error
         * and retransmit this object. Remember to manually free the buffer !
         */
        p_req->callback.write((void*)p_req->write.p_data);
        return;
    }

    /* Update the CRC of the firmware image. */
    s_dfu_settings.write_offset                   += p_req->write.len;
    s_dfu_settings.progress.firmware_image_offset += p_req->write.len;
    s_dfu_settings.progress.firmware_image_crc     = next_crc;

    /* This is only used when the PRN is triggered and the 'write' message
     * is answered with a CRC message and these field are copied into the response.
     */
    p_res->write.crc    = s_dfu_settings.progress.firmware_image_crc;
    p_res->write.offset = s_dfu_settings.progress.firmware_image_offset;
}


static void on_data_obj_crc_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
{
    NRF_LOG_DEBUG("Handle NRF_DFU_OP_CRC_GET (data)");
    NRF_LOG_DEBUG("Offset:%d, CRC:0x%08x",
                 s_dfu_settings.progress.firmware_image_offset,
                 s_dfu_settings.progress.firmware_image_crc);

    p_res->crc.crc    = s_dfu_settings.progress.firmware_image_crc;
    p_res->crc.offset = s_dfu_settings.progress.firmware_image_offset;
}

/*
 * Function that validates firmware in external memory and writes to internal memory 
 */
//static void on_data_obj_execute_external_memory(void)
//{
//    ret_code_t          ret;
//    nrf_dfu_response_t res =
//    {
//        .request = NRF_DFU_OP_OBJECT_EXECUTE,
//    };

//    //if the offset is equal to the expected image size
//    if (s_dfu_settings.progress.firmware_image_offset == m_firmware_size_req)
//    {
//        NRF_LOG_DEBUG("Whole firmware image received. Postvalidating NOR flash.");

//        res.result = nrf_dfu_validation_post_data_execute(0x12000000, m_firmware_size_req); //validate code in external memory
//        res.result = ext_err_code_handle(res.result);//check if validation had any errors

//        if(res.result ==NRF_DFU_RES_CODE_SUCCESS)//if validation successful
//        {
//            //erase internal flash space for firmware
//            if (nrf_dfu_flash_erase(m_firmware_start_addr,
//                            CEIL_DIV(m_firmware_size_req, CODE_PAGE_SIZE), NULL) != NRF_SUCCESS)
//            {
//                NRF_LOG_ERROR("Erase operation failed");
//                res.result = NRF_DFU_RES_CODE_INVALID_OBJECT;
//                return;
//            }

//            int image = 0;
//            while( image != m_firmware_size_req)
//            {
//               uint32_t write_addr = m_firmware_start_addr + image;//offset flash address
//               memset(m_rx_buff,0,sizeof(m_rx_buff));//clear buffer 
//               if((image) < m_firmware_size_req)//if the current image size is less that the required image size
//               {
//                  ret = nrf_drv_qspi_read(m_rx_buff,sizeof(m_rx_buff),image);//read qspi
//                  APP_ERROR_CHECK(ret);
//                  nrf_delay_ms(50);//wait for read function to be done
//                  ret = nrf_dfu_flash_store(write_addr,m_rx_buff, sizeof(m_rx_buff), NULL);//store memory in flash
//                  APP_ERROR_CHECK(ret);
//                  image = image + 64; //increment current image size by the required mtu size
//               }
//               else
//               {
//                  ret = nrf_drv_qspi_read(m_rx_buff,sizeof(m_firmware_size_req-image),image);//read the rest of the firmware stored in memory
//                  APP_ERROR_CHECK(ret);
//                  nrf_delay_ms(50);
//                  ret = nrf_dfu_flash_store(write_addr,m_rx_buff,sizeof(m_firmware_size_req-image),NULL);//write the rest of the firmware to flash
//                  APP_ERROR_CHECK(ret);
//                  image = m_firmware_size_req;//set the image size equal to the required firmware size
//               }
//               printf("Current image size: %i\n",image);
//            }
//          nrf_drv_qspi_uninit();//uninitialize qspi 
//        }
//    }
//}

static void on_data_obj_execute_request_sched(void * p_evt, uint16_t event_length)
{
    UNUSED_PARAMETER(event_length);

    ret_code_t          ret;
    nrf_dfu_request_t * p_req = (nrf_dfu_request_t *)(p_evt);

    /* Wait for all buffers to be written in flash. */
    if (nrf_fstorage_is_busy(NULL))
    {
        ret = app_sched_event_put(p_req, sizeof(nrf_dfu_request_t), on_data_obj_execute_request_sched);
        if (ret != NRF_SUCCESS)
        {
            NRF_LOG_ERROR("Failed to schedule object execute: 0x%x.", ret);
        }
        return;
    }

    nrf_dfu_response_t res =
    {
        .request = NRF_DFU_OP_OBJECT_EXECUTE,
    };

    if (s_dfu_settings.progress.firmware_image_offset == m_firmware_size_req)
    {
        NRF_LOG_DEBUG("Whole firmware image received. Postvalidating.");

        #if NRF_DFU_IN_APP
        res.result = nrf_dfu_validation_post_data_execute(m_firmware_start_addr, m_firmware_size_req);
        #else
        res.result = nrf_dfu_validation_activation_prepare(m_firmware_start_addr, m_firmware_size_req);
        #endif

        res.result = ext_err_code_handle(res.result);

        /* Provide response to transport */
        p_req->callback.response(&res, p_req->p_context);

        ret = nrf_dfu_settings_write_and_backup((nrf_dfu_flash_callback_t)on_dfu_complete);
        UNUSED_RETURN_VALUE(ret);
    }
    else
    {
        res.result = NRF_DFU_RES_CODE_SUCCESS;

        /* Provide response to transport */
        p_req->callback.response(&res, p_req->p_context);

        if (NRF_DFU_SAVE_PROGRESS_IN_FLASH)
        {
            /* Allowing skipping settings backup to save time and flash wear. */
            ret = nrf_dfu_settings_write_and_backup(NULL);
            UNUSED_RETURN_VALUE(ret);
        }
    }

    NRF_LOG_DEBUG("Request handling complete. Result: 0x%x", res.result);
}


static bool on_data_obj_execute_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
{
    NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_EXECUTE (data)");

    uint32_t const data_object_size = s_dfu_settings.progress.firmware_image_offset -
                                      s_dfu_settings.progress.firmware_image_offset_last;

    if (s_dfu_settings.progress.data_object_size != data_object_size)
    {
        /* The size of the written object was not as expected. */
        NRF_LOG_ERROR("Invalid data. expected: %d, got: %d",
                      s_dfu_settings.progress.data_object_size,
                      data_object_size);

        p_res->result = NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED;
        return true;
    }

    /* Update the offset and crc values for the last object written. */
    s_dfu_settings.progress.data_object_size           = 0;
    s_dfu_settings.progress.firmware_image_crc_last    = s_dfu_settings.progress.firmware_image_crc;
    s_dfu_settings.progress.firmware_image_offset_last = s_dfu_settings.progress.firmware_image_offset;

    //on_data_obj_execute_external_memory();

    on_data_obj_execute_request_sched(p_req, 0);

    m_observer(NRF_DFU_EVT_OBJECT_RECEIVED);

    return false;
}


bool nrf_dfu_data_req(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
{
    ASSERT(p_req);
    ASSERT(p_res);

    bool response_ready = true;

    switch (p_req->request)
    {
        case NRF_DFU_OP_OBJECT_CREATE:
        {
            on_data_obj_create_request(p_req, p_res);
        } break;

        case NRF_DFU_OP_OBJECT_WRITE:
        {
            on_data_obj_write_request(p_req, p_res);
        } break;

        case NRF_DFU_OP_CRC_GET:
        {
            on_data_obj_crc_request(p_req, p_res);
        } break;

        case NRF_DFU_OP_OBJECT_EXECUTE:
        {
            response_ready = on_data_obj_execute_request(p_req, p_res);
        } break;

        case NRF_DFU_OP_OBJECT_SELECT:
        {
            on_data_obj_select_request(p_req, p_res);
        } break;

        default:
        {
            ASSERT(false);
        } break;
    }

    return response_ready;
}


/**@brief Function for handling requests to manipulate data or command objects.
 *
 * @param[in]  p_req    Request.
 * @param[out] p_res    Response.
 *
 * @return  Whether response is ready to be sent.
 */
static bool nrf_dfu_obj_op(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
{
    /* Keep track of the current object type since write and execute requests don't contain it. */
    static nrf_dfu_obj_type_t current_object = NRF_DFU_OBJ_TYPE_COMMAND;

    if (    (p_req->request == NRF_DFU_OP_OBJECT_SELECT)
        ||  (p_req->request == NRF_DFU_OP_OBJECT_CREATE))
    {
        STATIC_ASSERT(offsetof(nrf_dfu_request_select_t, object_type) ==
                      offsetof(nrf_dfu_request_create_t, object_type),
                      "Wrong object_type offset!");

        current_object = (nrf_dfu_obj_type_t)(p_req->select.object_type);
    }

    bool response_ready = true;

    switch (current_object)
    {
        case NRF_DFU_OBJ_TYPE_COMMAND:
            nrf_dfu_command_req(p_req, p_res);
            break;

        case NRF_DFU_OBJ_TYPE_DATA:
            response_ready = nrf_dfu_data_req(p_req, p_res);
            break;

        default:
            /* The select request had an invalid object type. */
            NRF_LOG_ERROR("Invalid object type in request.");
            current_object = NRF_DFU_OBJ_TYPE_INVALID;
            p_res->result  = NRF_DFU_RES_CODE_INVALID_OBJECT;
            break;
    }

    return response_ready;
}


static void nrf_dfu_req_handler_req_process(nrf_dfu_request_t * p_req)
{
    ASSERT(p_req->callback.response);

    bool response_ready = true;

    /* The request handlers assume these values to be set. */
    nrf_dfu_response_t response =
    {
        .request = p_req->request,
        .result  = NRF_DFU_RES_CODE_SUCCESS,
    };


    switch (p_req->request)
    {
#if !NRF_DFU_PROTOCOL_REDUCED
        case NRF_DFU_OP_PROTOCOL_VERSION:
        {
            on_protocol_version_request(p_req, &response);
        } break;

        case NRF_DFU_OP_HARDWARE_VERSION:
        {
            on_hw_version_request(p_req, &response);
        } break;

        case NRF_DFU_OP_FIRMWARE_VERSION:
        {
            on_fw_version_request(p_req, &response);
        } break;

        case NRF_DFU_OP_PING:
        {
            on_ping_request(p_req, &response);
        } break;

        case NRF_DFU_OP_MTU_GET:
        {
            on_mtu_get_request(p_req, &response);
        } break;
#endif
        case NRF_DFU_OP_RECEIPT_NOTIF_SET:
        {
            on_prn_set_request(p_req, &response);
        } break;

        case NRF_DFU_OP_ABORT:
        {
            on_abort_request(p_req, &response);
        } break;

        case NRF_DFU_OP_OBJECT_CREATE:
            /* Restart the inactivity timer on CREATE messages. */
            /* Fallthrough. */
        case NRF_DFU_OP_OBJECT_SELECT:
        case NRF_DFU_OP_OBJECT_WRITE:
        case NRF_DFU_OP_OBJECT_EXECUTE:
        case NRF_DFU_OP_CRC_GET:
        {
            response_ready = nrf_dfu_obj_op(p_req, &response);
        } break;

        default:
            NRF_LOG_INFO("Invalid opcode received: 0x%x.", p_req->request);
            response.result = NRF_DFU_RES_CODE_OP_CODE_NOT_SUPPORTED;
            break;
    }

    if (response_ready)
    {
        NRF_LOG_DEBUG("Request handling complete. Result: 0x%x", response.result);

        p_req->callback.response(&response, p_req->p_context);

        if (response.result != NRF_DFU_RES_CODE_SUCCESS)
        {
            m_observer(NRF_DFU_EVT_DFU_FAILED);
        }
    }
}


static void nrf_dfu_req_handler_req(void * p_evt, uint16_t event_length)
{
    nrf_dfu_request_t * p_req = (nrf_dfu_request_t *)(p_evt);
    nrf_dfu_req_handler_req_process(p_req);
}


ret_code_t nrf_dfu_req_handler_on_req(nrf_dfu_request_t * p_req)
{
    ret_code_t ret;

    if (p_req->callback.response == NULL)
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    ret = app_sched_event_put(p_req, sizeof(nrf_dfu_request_t), nrf_dfu_req_handler_req);
    if (ret != NRF_SUCCESS)
    {
        NRF_LOG_WARNING("Scheduler ran out of space!");
    }

    return ret;
}


ret_code_t nrf_dfu_req_handler_init(nrf_dfu_observer_t observer)
{
    ret_code_t       ret_val;
    nrf_dfu_result_t result;

    if (observer == NULL)
    {
        return NRF_ERROR_INVALID_PARAM;
    }

#if defined(BLE_STACK_SUPPORT_REQD) || defined(ANT_STACK_SUPPORT_REQD)
    ret_val  = nrf_dfu_flash_init(true);
#else
    ret_val = nrf_dfu_flash_init(false);
#endif
    if (ret_val != NRF_SUCCESS)
    {
        return ret_val;
    }

    nrf_dfu_validation_init();
    if (nrf_dfu_validation_init_cmd_present())
    {
        /* Execute a previously received init packed. Subsequent executes will have no effect. */
        result = nrf_dfu_validation_init_cmd_execute(&m_firmware_start_addr, &m_firmware_size_req);
        if (result != NRF_DFU_RES_CODE_SUCCESS)
        {
            /* Init packet in flash is not valid! */
            return NRF_ERROR_INTERNAL;
        }
    }

    m_observer = observer;

    /* Initialize extended error handling with "No error" as the most recent error. */
    result = ext_error_set(NRF_DFU_EXT_ERROR_NO_ERROR);
    UNUSED_RETURN_VALUE(result);

    return NRF_SUCCESS;
}
nrf_dfu_handling_error.h

  • Hello,

    The hash is meant to be computed on the received FW image and then compared against the hash from the init packet as a part of the post-validation step before the new update is activated.

    Has the new FW image been copied to internal flash at this point, or is still located only your external SPI flash? If it's not in internal flash, then you will need to modify the nrf_dfu_validation_hash_ok() to read the data over SPI.

    Best regards,

    Vidar

  • At this point the firmware has been copied into internal memory, however, I still have not erased it from external memory. Since I am doing it in internal memory, I should not need to modify the hash correct?

  • No, you shouldn't have to modify the hash. I think the question is why the computed hash value is not matching the hash value from the init packet. Have you verified that the image is copied correctly from external flash and that there aren't any bit errors?

    Edit: were you using QSPI flash? In that case, you can use nrfjprog to compare the memory content.

    Reading application image stored in QSPI flash

    nrfjprog --memrd 0x12000000 (+offset?) --n <size of FW image in bytes> > FW_image_ext_flash.txt

    Reading application image stored in bank 0

    nrfjprog --memrd < bank 0 address> --n <size of FW image in bytes> > FW_image_internal_flash.txt

  • I am using qspi flash as you can see in my nrf_dfu.c line 193.

    Below is an how I am using qspi to load into a buffer that is used to write to flash.

    //Write Command - writing from external to internal memory 
        uint32_t read_addr = APPLICATION_IMAGE_LOCATION; //start reading from the firmware image location in external memory
        p_req.request = NRF_DFU_OP_OBJECT_WRITE;//change the opcode to write 
        while(s_dfu_settings.write_offset!=len)//while offset does not equal the length
        {//s_dfu_settings.write_offset is altered in nrf_dfu_req_handler. DO NOT MODIFY IT HERE!!!
            memset(m_rx_buf,0,sizeof(m_rx_buf));//clear buffer
            if((s_dfu_settings.write_offset + mtu_size) < len)//if the current image size is less that the required image size
            {
                nrf_drv_qspi_read(m_rx_buf, mtu_size ,read_addr + s_dfu_settings.write_offset);//read MTU_size bytes of firmware packet from external memory into buffer starting at firmware packet location 
                nrf_delay_ms(5);//flush delay
                p_req.write.p_data = m_rx_buf;//initialize the data to be sent from the transfer buffer
                nrf_delay_ms(5);//flush delay
                p_req.write.len = mtu_size;//set the length to the size of the MTU
            }
            else
            {
                nrf_drv_qspi_read(m_rx_buf, len - s_dfu_settings.write_offset ,read_addr + s_dfu_settings.write_offset);//read the rest of firmware packet from external memory into buffer
                nrf_delay_ms(5);//flush delay
                p_req.write.p_data = m_rx_buf;//initialize the data to be sent from the transfer buffer
                nrf_delay_ms(5);//flush delay
                p_req.write.len = len-s_dfu_settings.write_offset;//set the length to the size of the number of bytes left to read
            }
            nrf_dfu_data_req(&p_req,&p_res);
            //printf("Next s_dfu_settings.write_offset size: %i\n",s_dfu_settings.write_offset);//debug size of the firmware image after each iteration 
            free((void*)p_req.write.p_data);//free write.data space
        } 

    Below I have also attached the output for the commands you have given me above

    User@DESKTOP-60MHBSQ MINGW64 /d/Downloads/Segger/nRF5_SDK_15.3.0_59ac345/Bootloader/bootloader_secure_uart (main)
    $ nrfjprog --memrd 0x2003fe68 --n 200 p_req_write_p_data.txt
    0x2003FE68: 20040000 00001375 0000135D 0000135F   |... u...]..._...|
    0x2003FE78: 00001361 00001363 00001365 00000000   |a...c...e.......|
    0x2003FE88: 00000000 00000000 00000000 00001367   |............g...|
    0x2003FE98: 00001369 00000000 0000136B 0000136D   |i.......k...m...|
    0x2003FEA8: 0000136F 0000136F 0000136F 0000136F   |o...o...o...o...|
    0x2003FEB8: 0000136F 0000136F 0000136F 0000136F   |o...o...o...o...|
    0x2003FEC8: 0000136F 0000136F 0000136F 0000136F   |o...o...o...o...|
    0x2003FED8: 0000136F 0000136F 0000136F 0000136F   |o...o...o...o...|
    0x2003FEE8: 0000136F 0000136F 0000136F 0000136F   |o...o...o...o...|
    0x2003FEF8: 0000136F 0000136F 0000136F 0000136F   |o...o...o...o...|
    0x2003FF08: 0000136F 0000136F 0000136F 0000136F   |o...o...o...o...|
    0x2003FF18: 0000136F 0000136F 00000000 00000000   |o...o...........|
    0x2003FF28: 0000136F 0000136F                     |o...o...|
    
    User@DESKTOP-60MHBSQ MINGW64 /d/Downloads/Segger/nRF5_SDK_15.3.0_59ac345/Bootloader/bootloader_secure_uart (main)
    $ nrfjprog --memrd 0x12001000 --n 200 ext_mem_fw.txt
    0x12001000: 20040000 00001375 0000135D 0000135F   |... u...]..._...|
    0x12001010: 00001361 00001363 00001365 00000000   |a...c...e.......|
    0x12001020: 00000000 00000000 00000000 00001367   |............g...|
    0x12001030: 00001369 00000000 0000136B 0000136D   |i.......k...m...|
    0x12001040: 0000136F 0000136F 0000136F 0000136F   |o...o...o...o...|
    0x12001050: 0000136F 0000136F 0000136F 0000136F   |o...o...o...o...|
    0x12001060: 0000136F 0000136F 0000136F 0000136F   |o...o...o...o...|
    0x12001070: 0000136F 0000136F 0000136F 0000136F   |o...o...o...o...|
    0x12001080: 0000136F 0000136F 0000136F 0000136F   |o...o...o...o...|
    0x12001090: 0000136F 0000136F 0000136F 0000136F   |o...o...o...o...|
    0x120010A0: 0000136F 0000136F 0000136F 0000136F   |o...o...o...o...|
    0x120010B0: 0000136F 0000136F 00000000 00000000   |o...o...........|
    0x120010C0: 0000136F 0000136F                     |o...o...|
    
    User@DESKTOP-60MHBSQ MINGW64 /d/Downloads/Segger/nRF5_SDK_15.3.0_59ac345/Bootloader/bootloader_secure_uart (main)
    $
    

  • Edit: Another question, if commented out checking the hash, and set the istrue variable to true, would to code theoretically work?

Related