Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

nRF5 SDK custom bootloader, attempting flash write operation returns NRF_LOG_INTERNAL and NRF_ERROR_INVALID_ADDR

I'm writing a custom bootloader for an nRF52840 using the nRF5 SDK v15.0.0. This bootloader uses the SoftDevice to receive BLE packets containing binary data for the DFU. Write operations don't work and I can't trace back the source of the error, what could be going wrong with my code?

NRF_FSTORAGE_DEF(nrf_fstorage_t dfu_flash) =
    {
        .evt_handler = dfu_flash_callback,
        .start_addr = 0x000070000,
        .end_addr = 0x0000E0000,
};

void dfu_flash_callback(nrf_fstorage_evt_t *evt) {
    NRF_LOG_INFO("evt: %d result: %d", evt->id, evt->result);
}

void flash_manager_init(uint32_t fw_size, uint32_t fw_crc) {
    p_fs_api = &nrf_fstorage_sd;
    ret_code_t err_code = nrf_fstorage_init(&dfu_flash, p_fs_api, dfu_flash_callback);
    if (err_code == NRF_SUCCESS) {
        size_received = 77388; // temporary hardcoded value
        crc_received = fw_crc;
        uint32_t num_pages = MIN(CEIL_DIV(size_received, PAGE_SIZE), MAX_NUM_PAGES);
        err_code = nrf_fstorage_erase(&dfu_flash, dfu_flash.start_addr, num_pages, dfu_flash_callback);
        NRF_LOG_INFO("erase err: %d, pages: %d, bytes: 0x%X", err_code, num_pages, size_received);
    }
    is_dfu_started = (err_code == NRF_SUCCESS);
}

void flash_manager_write(uint8_t *data, uint32_t cur_idx, uint32_t len) {
    if (!is_dfu_started)
        return;

    payload_loc = dfu_flash.start_addr + cur_idx;

    static uint8_t binary_chunk[200];
    memcpy(binary_chunk, data, len);
    uint32_t err_code = nrf_fstorage_write(&dfu_flash, payload_loc, binary_chunk, len, dfu_flash_callback);
    NRF_LOG_INFO("idx: %d, payload loc: %p, length: %d, err: %d", cur_idx, payload_loc, len, err_code);
}

Direct calls fstorage functions all return NRF_SUCCESS, and the callback for flash erase works, but any flash write callbacks provide an error NRF_LOG_INTERNAL. I managed to trace the problem to this function in nrf_fstorage_sd.c:

static uint32_t write_execute(nrf_fstorage_sd_op_t const * p_op)
{
    uint32_t chunk_len;

    chunk_len = MIN(p_op->write.len - p_op->write.offset, NRF_FSTORAGE_SD_MAX_WRITE_SIZE);
    chunk_len = MAX(1, chunk_len / m_flash_info.program_unit);

    /* Cast to p_src to uint32_t to perform arithmetic. */
    uint32_t       * p_dest = (uint32_t*)(p_op->write.dest + p_op->write.offset);
    uint32_t const * p_src  = (uint32_t*)((uint32_t)p_op->write.p_src + p_op->write.offset);

    return sd_flash_write(p_dest, p_src, chunk_len);
}

Here, sd_flash_write returns NRF_ERROR_INVALID_ADDR, which seems to be the source of the problem. I couldn't narrow down the problem beyond this, because the only definition I found for sd_flash_write is in ser_app_hal_nrf5x.c, and the function from this library is not called (checked with print statements and modifying return values). I found some relevant macros in the SDK config file, but none of these seem like they could be causing the issue:

#ifndef NRF_FSTORAGE_ENABLED
#define NRF_FSTORAGE_ENABLED 1
#endif

#ifndef NRF_FSTORAGE_PARAM_CHECK_DISABLED
#define NRF_FSTORAGE_PARAM_CHECK_DISABLED 1
#endif

#ifndef NRF_FSTORAGE_SD_QUEUE_SIZE
#define NRF_FSTORAGE_SD_QUEUE_SIZE 16
#endif

#ifndef NRF_FSTORAGE_SD_MAX_RETRIES
#define NRF_FSTORAGE_SD_MAX_RETRIES 8
#endif

#ifndef NRF_FSTORAGE_SD_MAX_WRITE_SIZE
#define NRF_FSTORAGE_SD_MAX_WRITE_SIZE 4096
#endif

Parents
  • Hi

    Initially, this seems to be caused by trying to access the SoftDevice addresses which you're not allowed to read from or write to. What exactly is your use case for accessing the SoftDevice here? Is there a reason you can't use the bootloader available in the SDK already?

    Best regards,

    Simon

  • The SoftDevice is being used in the bootloader for a custom DFU process. I’m working with a complex design involving several modules and pre-existing bootloader functionality so I don’t have the ability to use the Nordic samples at this point in the project, but the current bootloader code is based on a Nordic template. Are there SoftDevice functions not accessible to me in the bootloader?

    I do sidestep the Nordic DFU libraries for my BLE initialization (https://devzone.nordicsemi.com/f/nordic-q-a/111298/implementing-custom-ble-transfer-process-in-bootloaderand now I’m wondering if that could be related to the problem.

  • Hi,

    Which address did you attempt to write to when you got NRF_ERROR_INVALID_ADDR? Are you able to log it or check with a debugger? You can get this error in a few different cases:

    • If the source or destination adsdress is not word aligned
    • If the destination address is outside of the physical address space of the device

    For reference, if you try to write over the SoftDevice itself, you will get a different error: NRF_ERROR_FORBIDDEN.

  • If there were a word alignment issue, wouldn't this be rejected on the initial call to nrf_fstorage_write rather the error code being passed into the callback handler? I attempt to write a 200-byte chunk into address 0x70000, and the source address comes from static uint8_t binary_chunk[200]. This process originally worked when it was used in the main app, but began reporting errors after I shifted the logic into the bootloader. The flash and RAM regions from the bootloader are taken from the secure bootloader sample in 15.0.0:

    MEMORY
    {
      FLASH (rx) : ORIGIN = 0xf1000, LENGTH = 0xd000
      RAM (rwx) :  ORIGIN = 0x200057b8, LENGTH = 0x3a848
      uicr_mbr_params_page (r) : ORIGIN = 0x10001018, LENGTH = 0x4
      mbr_params_page (r) : ORIGIN = 0x000FE000, LENGTH = 0x1000
      bootloader_settings_page (r) : ORIGIN = 0x000FF000, LENGTH = 0x1000
      uicr_bootloader_start_address (r) : ORIGIN = 0x10001014, LENGTH = 0x4
    }

Reply
  • If there were a word alignment issue, wouldn't this be rejected on the initial call to nrf_fstorage_write rather the error code being passed into the callback handler? I attempt to write a 200-byte chunk into address 0x70000, and the source address comes from static uint8_t binary_chunk[200]. This process originally worked when it was used in the main app, but began reporting errors after I shifted the logic into the bootloader. The flash and RAM regions from the bootloader are taken from the secure bootloader sample in 15.0.0:

    MEMORY
    {
      FLASH (rx) : ORIGIN = 0xf1000, LENGTH = 0xd000
      RAM (rwx) :  ORIGIN = 0x200057b8, LENGTH = 0x3a848
      uicr_mbr_params_page (r) : ORIGIN = 0x10001018, LENGTH = 0x4
      mbr_params_page (r) : ORIGIN = 0x000FE000, LENGTH = 0x1000
      bootloader_settings_page (r) : ORIGIN = 0x000FF000, LENGTH = 0x1000
      uicr_bootloader_start_address (r) : ORIGIN = 0x10001014, LENGTH = 0x4
    }

Children
  • Hi,

    I agree with your resoning, fstorage also checks for word alignment and boundaries. However, I do not see any other reason for getting NRF_ERROR_INVALID_ADDR returned from a call to sd_flash_write() when looking at the SoftDevice implementation. Can you log the parameters you pass to each call to sd_flash_write() so that we can see which exact parameters you call it with when you get the unexpected NRF_ERROR_INVALID_ADDR returned?

Related