Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

FDS - data corruption during an interrupted swap

Hi,

I have found a bug in the fds module (Flash Data Storage), which lead to losing "installed pages". If it was a swap page, fds module will be bricked.

Tested on SDK 14.2.0, but it is present in 15.2.0. Hardware is not relevant, but I tested it with PCA10040 (nRF52832).

Pages used by fds are taged with 2 magic words. Please see code below:

// Tags a page as swap, i.e., reserved for GC.
static ret_code_t page_tag_write_swap(void)
{
    // The tag needs to be statically allocated since it is not buffered by fstorage.
    static uint32_t const page_tag_swap[] = {FDS_PAGE_TAG_MAGIC, FDS_PAGE_TAG_SWAP};
    return nrf_fstorage_write(&m_fs, (uint32_t)m_swap_page.p_addr, page_tag_swap, FDS_PAGE_TAG_SIZE * sizeof(uint32_t), NULL);
}


// Tags a page as data, i.e, ready for storage.
static ret_code_t page_tag_write_data(uint32_t const * const p_page_addr)
{
    // The tag needs to be statically allocated since it is not buffered by fstorage.
    static uint32_t const page_tag_data[] = {FDS_PAGE_TAG_MAGIC, FDS_PAGE_TAG_DATA};
    return nrf_fstorage_write(&m_fs, (uint32_t)p_page_addr, page_tag_data, FDS_PAGE_TAG_SIZE * sizeof(uint32_t), NULL);
}

The problem occures, when this process is interrupted (with reset). It may result in writing only first word:

#define FDS_PAGE_TAG_MAGIC      (0xDEADC0DE)

After reset, fds is checking this magic tag to determine page type. It uses these functions:

// Reads a page tag, and determines if the page is used to store data or as swap.
static fds_page_type_t page_identify(uint32_t const * const p_page_addr)
{
    if (   (p_page_addr == NULL)    // Should never happen.
        || (p_page_addr[FDS_PAGE_TAG_WORD_0] != FDS_PAGE_TAG_MAGIC))
    {
        return FDS_PAGE_UNDEFINED;
    }

    switch (p_page_addr[FDS_PAGE_TAG_WORD_1])
    {
        case FDS_PAGE_TAG_SWAP:
            return FDS_PAGE_SWAP;

        case FDS_PAGE_TAG_DATA:
            return FDS_PAGE_DATA;

        default:
            return FDS_PAGE_UNDEFINED;
    }
}


static bool page_is_erased(uint32_t const * const p_page_addr)
{
    for (uint32_t i = 0; i < FDS_PAGE_SIZE; i++)
    {
        if (*(p_page_addr + i) != FDS_ERASED_WORD)
        {
            return false;
        }
    }

    return true;
}

This fuction will classify this page as FDS_PAGE_UNDEFINED and unerased.

I would like to propose a change in page_is_erased() function.

static bool page_is_erased(uint32_t const * const p_page_addr)
{
    if ((p_page_addr[FDS_PAGE_TAG_WORD_0] != FDS_ERASED_WORD) &&
        (p_page_addr[FDS_PAGE_TAG_WORD_0] != FDS_PAGE_TAG_MAGIC))
    {
        return false;
    }

    for (uint32_t i = FDS_PAGE_TAG_WORD_1; i < FDS_PAGE_SIZE; i++)
    {
        if (*(p_page_addr + i) != FDS_ERASED_WORD)
        {
            return false;
        }
    }

    return true;
}

Thanks to that, module will correctly classify this page as FDS_PAGE_ERASED.

Related