core/flash/flash_store_contiguous_blocks.c (634 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "flash_store_contiguous_blocks.h"
#include "flash_util.h"
#include "common/buffer_util.h"
#include "common/unused.h"
/**
* The maximum amount of data allowed in a single data block.
*/
#define FLASH_STORE_MAX_DATA_SIZE ((64 * 1024) - 1)
/**
* Verify that parameters are valid for writing to a flash data block.
*
* @param flash_store The flash where the data will be written.
* @param id Block ID of the data.
* @param data The data to write.
* @param length Length of the data.
*
* @return 0 if the parameters are valid or an error code.
*/
int flash_store_contiguous_blocks_verify_write_params (
const struct flash_store_contiguous_blocks *flash, int id, const uint8_t *data, size_t length)
{
if ((flash == NULL) || (data == NULL) || (length == 0)) {
return FLASH_STORE_INVALID_ARGUMENT;
}
if ((id < 0) || ((uint32_t) id >= flash->state->blocks)) {
return FLASH_STORE_UNSUPPORTED_ID;
}
if (!flash->variable && (length != flash->state->max_size)) {
return FLASH_STORE_BAD_DATA_LENGTH;
}
else if (length > flash->state->max_size) {
return FLASH_STORE_BAD_DATA_LENGTH;
}
return 0;
}
/**
* Write a block of data to flash, including any data for internal use. Parameters must have been
* prevalidated.
*
* @param flash The flash where the data should be written.
* @param id Block ID of the data.
* @param data The data to write.
* @param length Length of the data.
* @param extra_data Extra data to append to the end of the data block. The flash store must have
* been initialized to expect this extra data. Can be null if no extra data is necessary.
* @param extra_length Length of the extra data being written.
*
* @return 0 if the data was written successfully or an error code.
*/
int flash_store_contiguous_blocks_write_common (const struct flash_store_contiguous_blocks *flash,
int id, const uint8_t *data, size_t length, const uint8_t *extra_data, size_t extra_length)
{
int base_offset;
int offset;
int status;
base_offset = id * flash->state->block_size;
if (flash->decreasing) {
base_offset = -base_offset;
}
offset = base_offset;
status = flash_sector_erase_region (flash->flash, flash->base_addr + base_offset,
flash->state->block_size);
if (status != 0) {
return status;
}
#ifdef FLASH_STORE_SUPPORT_NO_PARTIAL_PAGE_WRITE
if (flash->state->page_buffer) {
/* It is necessary to ensure that no page is written more than once. Internal buffering is
* necessary in three cases:
* 1. The first page of data for variable length storage, since that holds the data header.
* 2. The last page of data for storage that appends extra data to the end, but only in
* cases where the stored data does not align to page boundaries.
* 3. The entire amount of data (header, data, and extra data) fits into the first page.
*
* Data that is naturally aligned to full pages is not buffered and is written directly from
* the source data. */
struct flash_store_header header = {
.header_len = FLASH_STORE_HEADER_LENGTH,
.marker = FLASH_STORE_HEADER_MARKER,
.length = length
};
size_t header_len = (!flash->variable) ? 0 :
(flash->state->old_header) ? sizeof (header.length) : sizeof (header);
size_t first = flash->state->page_size - header_len;
size_t remain = FLASH_REGION_OFFSET (length + header_len, flash->state->page_size);
size_t write_extra = flash->state->page_size - remain;
size_t middle = (length > remain) ? (length - remain) : length;
bool extra_first = false;
if (flash->variable) {
if (middle > first) {
middle -= first;
}
else {
if (middle < first) {
/* All the data fits into the first flash page. */
extra_first = true;
}
first = middle;
middle = 0;
}
}
else {
if (middle < first) {
middle = 0;
}
first = 0;
}
if (extra_length == 0) {
if (!extra_first) {
middle += remain;
}
remain = 0;
write_extra = 0;
}
else if (write_extra > extra_length) {
write_extra = extra_length;
}
/* Write full pages of source data. */
offset += first + header_len;
if (middle != 0) {
status = flash_write_and_verify (flash->flash, flash->base_addr + offset, &data[first],
middle);
if (status != 0) {
return status;
}
offset += middle;
}
/* Write the additional data appended to the end. */
if (!extra_first && (extra_length != 0)) {
platform_mutex_lock (&flash->state->lock);
memcpy (flash->state->page_buffer, &data[length - remain], remain);
memcpy (&flash->state->page_buffer[remain], extra_data, write_extra);
status = flash_write_and_verify (flash->flash, flash->base_addr + offset,
flash->state->page_buffer, remain + write_extra);
platform_mutex_unlock (&flash->state->lock);
if (status != 0) {
return status;
}
offset += remain + write_extra;
}
else {
offset += write_extra;
}
if (write_extra < extra_length) {
status = flash_write_and_verify (flash->flash, flash->base_addr + offset,
&extra_data[write_extra], extra_length - write_extra);
if (status != 0) {
return status;
}
}
/* For variable storage, write the first page with the data header. */
if (first != 0) {
platform_mutex_lock (&flash->state->lock);
if (!flash->state->old_header) {
memcpy (flash->state->page_buffer, (uint8_t*) &header, sizeof (header));
}
else {
memcpy (flash->state->page_buffer, (uint8_t*) &header.length,
sizeof (header.length));
}
memcpy (&flash->state->page_buffer[header_len], data, first);
if (extra_first) {
memcpy (&flash->state->page_buffer[header_len + first], extra_data, write_extra);
header_len += write_extra;
}
status = flash_write_and_verify (flash->flash, flash->base_addr + base_offset,
flash->state->page_buffer, first + header_len);
platform_mutex_unlock (&flash->state->lock);
if (status != 0) {
return status;
}
}
}
else
#endif
{
/* Each page can be written multiple times without erasing. */
if (flash->variable) {
if (!flash->state->old_header) {
offset += FLASH_STORE_HEADER_LENGTH;
}
else {
offset += sizeof (uint16_t);
}
}
status = flash_write_and_verify (flash->flash, flash->base_addr + offset, data, length);
if (status != 0) {
return status;
}
if (extra_data) {
offset += length;
status = flash_write_and_verify (flash->flash, flash->base_addr + offset, extra_data,
extra_length);
if (status != 0) {
return status;
}
}
if (flash->variable) {
struct flash_store_header header = {
.header_len = FLASH_STORE_HEADER_LENGTH,
.marker = FLASH_STORE_HEADER_MARKER,
.length = length
};
if (!flash->state->old_header) {
status = flash_write_and_verify (flash->flash, flash->base_addr + base_offset,
(uint8_t*) &header, sizeof (header));
}
else {
status = flash_write_and_verify (flash->flash, flash->base_addr + base_offset,
(uint8_t*) &header.length, sizeof (header.length));
}
if (status != 0) {
return status;
}
}
}
return 0;
}
int flash_store_contiguous_blocks_write_no_hash (const struct flash_store *flash_store, int id,
const uint8_t *data, size_t length)
{
const struct flash_store_contiguous_blocks *flash =
(const struct flash_store_contiguous_blocks*) flash_store;
int status;
status = flash_store_contiguous_blocks_verify_write_params (flash, id, data, length);
if (status != 0) {
return status;
}
return flash_store_contiguous_blocks_write_common (flash, id, data, length, NULL, 0);
}
int flash_store_contiguous_blocks_write_with_hash (const struct flash_store *flash_store, int id,
const uint8_t *data, size_t length)
{
const struct flash_store_contiguous_blocks *flash =
(const struct flash_store_contiguous_blocks*) flash_store;
uint8_t hash[SHA256_HASH_LENGTH];
int status;
status = flash_store_contiguous_blocks_verify_write_params (flash, id, data, length);
if (status != 0) {
return status;
}
status = flash->hash->calculate_sha256 (flash->hash, data, length, hash, sizeof (hash));
if (status != 0) {
return status;
}
return flash_store_contiguous_blocks_write_common (flash, id, data, length, hash,
sizeof (hash));
}
/**
* Read the header on variable length data.
*
* @param flash The flash store that manages contiguous blocks of memory.
* @param offset Address offset to read the header from.
* @param header Output for the header data.
*
* @return 0 if the header was read and is valid or an error code.
*/
static int flash_store_contiguous_blocks_read_header (
const struct flash_store_contiguous_blocks *flash, int offset,
struct flash_store_header *header)
{
uint16_t old_length;
int status;
status = flash->flash->read (flash->flash, flash->base_addr + offset, (uint8_t*) header,
sizeof (struct flash_store_header));
if (status != 0) {
return status;
}
if (header->marker != FLASH_STORE_HEADER_MARKER) {
/* If the header marker does not match, we need to check the older format for backwards
* compatibility. If the first two bytes represent a valid length, assume the data is
* stored in the old way. This is not a perfect check since it could be corrupt in a way
* that looks valid. At that point, we would count on the hash to catch this corruption. */
old_length = *((uint16_t*) header);
if (old_length > flash->state->max_size) {
return FLASH_STORE_NO_DATA;
}
header->length = old_length;
header->header_len = 2;
}
else if ((header->header_len < FLASH_STORE_HEADER_MIN_LENGTH) ||
(header->length > flash->state->max_size)) {
return FLASH_STORE_NO_DATA;
}
return 0;
}
/**
* Read a block of data from flash.
*
* @param flash The flash that contains the requested data.
* @param id Block ID of the data.
* @param data Output buffer for the data.
* @param length Length of the data buffer.
* @param extra_data Output buffer for extra data that should be read from the data block. Set to
* null to read no extra data.
* @param extra_length Length of the extra data to read.
* @param out_length Output for the length of data that was read.
*
* @return 0 if the data was read successfully or an error code.
*/
int flash_store_contiguous_blocks_read_common (const struct flash_store_contiguous_blocks *flash,
int id, uint8_t *data, size_t length, uint8_t *extra_data, size_t extra_length,
size_t *out_length)
{
int offset;
int status;
if ((flash == NULL) || (data == NULL)) {
return FLASH_STORE_INVALID_ARGUMENT;
}
if ((id < 0) || ((uint32_t) id >= flash->state->blocks)) {
return FLASH_STORE_UNSUPPORTED_ID;
}
if (!flash->variable && (length < flash->state->max_size)) {
return FLASH_STORE_BUFFER_TOO_SMALL;
}
offset = id * flash->state->block_size;
if (flash->decreasing) {
offset = -offset;
}
if (flash->variable) {
struct flash_store_header header;
status = flash_store_contiguous_blocks_read_header (flash, offset, &header);
if (status != 0) {
return status;
}
if (length < header.length) {
return FLASH_STORE_BUFFER_TOO_SMALL;
}
offset += header.header_len;
length = header.length;
}
else {
length = flash->state->max_size;
}
status = flash->flash->read (flash->flash, flash->base_addr + offset, data, length);
if (status != 0) {
return status;
}
if (extra_data) {
offset += length;
status = flash->flash->read (flash->flash, flash->base_addr + offset, extra_data,
extra_length);
if (status != 0) {
return status;
}
}
*out_length = length;
return 0;
}
int flash_store_contiguous_blocks_read_no_hash (const struct flash_store *flash_store, int id,
uint8_t *data, size_t length)
{
const struct flash_store_contiguous_blocks *flash =
(const struct flash_store_contiguous_blocks*) flash_store;
int status;
status = flash_store_contiguous_blocks_read_common (flash, id, data, length, NULL, 0, &length);
return (status == 0) ? (int) length : status;
}
int flash_store_contiguous_blocks_read_with_hash (const struct flash_store *flash_store, int id,
uint8_t *data, size_t length)
{
const struct flash_store_contiguous_blocks *flash =
(const struct flash_store_contiguous_blocks*) flash_store;
uint8_t hash_mem[SHA256_HASH_LENGTH];
uint8_t hash_flash[SHA256_HASH_LENGTH];
int status;
status = flash_store_contiguous_blocks_read_common (flash, id, data, length, hash_flash,
sizeof (hash_flash), &length);
if (status != 0) {
return status;
}
status = flash->hash->calculate_sha256 (flash->hash, data, length, hash_mem, sizeof (hash_mem));
if (status != 0) {
return status;
}
if (buffer_compare (hash_mem, hash_flash, SHA256_HASH_LENGTH) != 0) {
return FLASH_STORE_CORRUPT_DATA;
}
return length;
}
int flash_store_contiguous_blocks_erase (const struct flash_store *flash_store, int id)
{
int offset;
const struct flash_store_contiguous_blocks *flash =
(const struct flash_store_contiguous_blocks*) flash_store;
if (flash == NULL) {
return FLASH_STORE_INVALID_ARGUMENT;
}
if ((id < 0) || ((uint32_t) id >= flash->state->blocks)) {
return FLASH_STORE_UNSUPPORTED_ID;
}
offset = id * flash->state->block_size;
if (flash->decreasing) {
offset = -offset;
}
return flash_sector_erase_region_and_verify (flash->flash, flash->base_addr + offset,
flash->state->block_size);
}
int flash_store_contiguous_blocks_erase_all (const struct flash_store *flash_store)
{
int offset = 0;
const struct flash_store_contiguous_blocks *flash =
(const struct flash_store_contiguous_blocks*) flash_store;
if (flash == NULL) {
return FLASH_STORE_INVALID_ARGUMENT;
}
if (flash->decreasing) {
offset = flash->state->block_size * (flash->state->blocks - 1);
}
return flash_sector_erase_region_and_verify (flash->flash, flash->base_addr - offset,
flash->state->block_size * flash->state->blocks);
}
int flash_store_contiguous_blocks_get_data_length (const struct flash_store *flash_store, int id)
{
const struct flash_store_contiguous_blocks *flash =
(const struct flash_store_contiguous_blocks*) flash_store;
if (flash == NULL) {
return FLASH_STORE_INVALID_ARGUMENT;
}
if ((id < 0) || ((uint32_t) id >= flash->state->blocks)) {
return FLASH_STORE_UNSUPPORTED_ID;
}
if (flash->variable) {
struct flash_store_header header;
int offset;
int status;
offset = id * flash->state->block_size;
if (flash->decreasing) {
offset = -offset;
}
status = flash_store_contiguous_blocks_read_header (flash, offset, &header);
if (status != 0) {
return status;
}
return header.length;
}
else {
return flash->state->max_size;
}
}
int flash_store_contiguous_blocks_has_data_stored (const struct flash_store *flash_store, int id)
{
const struct flash_store_contiguous_blocks *flash =
(const struct flash_store_contiguous_blocks*) flash_store;
if (flash == NULL) {
return FLASH_STORE_INVALID_ARGUMENT;
}
if ((id < 0) || ((uint32_t) id >= flash->state->blocks)) {
return FLASH_STORE_UNSUPPORTED_ID;
}
if (flash->variable) {
struct flash_store_header header;
int offset;
int status;
offset = id * flash->state->block_size;
if (flash->decreasing) {
offset = -offset;
}
status = flash_store_contiguous_blocks_read_header (flash, offset, &header);
switch (status) {
case 0:
return 1;
case FLASH_STORE_NO_DATA:
return 0;
default:
return status;
}
}
else {
return 1;
}
}
int flash_store_contiguous_blocks_get_max_data_length (const struct flash_store *flash_store)
{
const struct flash_store_contiguous_blocks *flash =
(const struct flash_store_contiguous_blocks*) flash_store;
if (flash == NULL) {
return FLASH_STORE_INVALID_ARGUMENT;
}
return flash->state->max_size;
}
int flash_store_contiguous_blocks_get_flash_size (const struct flash_store *flash_store)
{
const struct flash_store_contiguous_blocks *flash =
(const struct flash_store_contiguous_blocks*) flash_store;
if (flash == NULL) {
return FLASH_STORE_INVALID_ARGUMENT;
}
return flash->state->block_size * flash->state->blocks;
}
int flash_store_contiguous_blocks_get_num_blocks (const struct flash_store *flash_store)
{
const struct flash_store_contiguous_blocks *flash =
(const struct flash_store_contiguous_blocks*) flash_store;
if (flash == NULL) {
return FLASH_STORE_INVALID_ARGUMENT;
}
return flash->state->blocks;
}
/**
* Common API to initialize the variable state of flash store interface.
*
* @param store The flash storage to initialize.
* @param block_count The number of data blocks used for storage.
* @param data_length The minimum length of each data block.
* @param extra_data The length of extra internal data that will be added to each data block.
*
* @return 0 if the flash storage was successfully initialized or an error code.
*/
int flash_store_contiguous_blocks_init_state_common (
const struct flash_store_contiguous_blocks *store, size_t block_count, size_t data_length,
size_t extra_data)
{
uint32_t sector_size;
uint32_t device_size;
#ifdef FLASH_STORE_SUPPORT_NO_PARTIAL_PAGE_WRITE
uint32_t write_size;
#endif
int status;
if ((store == NULL) || (store->state == NULL) || (store->flash == NULL)) {
return FLASH_STORE_INVALID_ARGUMENT;
}
memset (store->state, 0, sizeof (struct flash_store_contiguous_blocks_state));
status = store->flash->get_sector_size (store->flash, §or_size);
if (status != 0) {
return status;
}
if (FLASH_REGION_OFFSET (store->base_addr, sector_size) != 0) {
return FLASH_STORE_STORAGE_NOT_ALIGNED;
}
status = store->flash->get_device_size (store->flash, &device_size);
if (status != 0) {
return status;
}
if ((store->base_addr >= device_size) || (store->decreasing && (store->base_addr == 0))) {
return FLASH_STORE_BAD_BASE_ADDRESS;
}
store->state->max_size = data_length;
store->state->blocks = block_count;
data_length += extra_data;
if (store->variable) {
data_length += FLASH_STORE_HEADER_LENGTH;
}
if (data_length > sector_size) {
store->state->block_size = data_length;
}
else {
store->state->block_size = sector_size;
}
store->state->block_size = (store->state->block_size + (sector_size - 1)) &
FLASH_REGION_MASK (sector_size);
if (!store->decreasing) {
if ((store->base_addr + (store->state->block_size * store->state->blocks)) > device_size) {
return FLASH_STORE_INSUFFICIENT_STORAGE;
}
}
else {
if ((store->state->block_size * (store->state->blocks - 1)) > store->base_addr) {
return FLASH_STORE_INSUFFICIENT_STORAGE;
}
}
if (store->variable) {
store->state->max_size = store->state->block_size - FLASH_STORE_HEADER_LENGTH - extra_data;
if (store->state->max_size > FLASH_STORE_MAX_DATA_SIZE) {
return FLASH_STORE_BLOCK_TOO_LARGE;
}
}
#ifdef FLASH_STORE_SUPPORT_NO_PARTIAL_PAGE_WRITE
status = store->flash->get_page_size (store->flash, &store->state->page_size);
if (status != 0) {
return status;
}
status = store->flash->minimum_write_per_page (store->flash, &write_size);
if (status != 0) {
return status;
}
if ((write_size != 1) &&
(store->variable || (!store->variable && extra_data &&
(FLASH_REGION_OFFSET (store->state->max_size, store->state->page_size) != 0)))) {
/* We need to buffer full page writes at the beginning and/or end of the data. */
store->state->page_buffer = platform_malloc (store->state->page_size);
if (store->state->page_buffer == NULL) {
return FLASH_STORE_NO_MEMORY;
}
}
status = platform_mutex_init (&store->state->lock);
if (status != 0) {
platform_free (store->state->page_buffer);
return status;
}
#endif
return 0;
}
/**
* Initialize flash storage for contiguous blocks of data.
*
* @param store The flash storage to initialize.
* @param state The flash store state to initialize.
* @param flash The flash device used for storage.
* @param base_addr The address of the first storage block. This must be aligned to a minimum erase
* block.
* @param block_count The number of data blocks used for storage.
* @param data_length The minimum length of each data block.
* @param decreasing Flag indicating if the storage grows down in the device address space.
* @param variable Flag indicating if the blocks contain variable length data.
*
* @return 0 if the flash storage was successfully initialized or an error code.
*/
int flash_store_contiguous_blocks_init_storage_common (struct flash_store_contiguous_blocks *store,
struct flash_store_contiguous_blocks_state *state, const struct flash *flash,
uint32_t base_addr, size_t block_count, size_t data_length, bool decreasing, bool variable)
{
if ((store == NULL) || (state == NULL) || (flash == NULL)) {
return FLASH_STORE_INVALID_ARGUMENT;
}
if (block_count == 0) {
return FLASH_STORE_NO_STORAGE;
}
if (data_length > FLASH_STORE_MAX_DATA_SIZE) {
return FLASH_STORE_BLOCK_TOO_LARGE;
}
memset (store, 0, sizeof (struct flash_store_contiguous_blocks));
store->base.erase = flash_store_contiguous_blocks_erase;
store->base.erase_all = flash_store_contiguous_blocks_erase_all;
store->base.get_data_length = flash_store_contiguous_blocks_get_data_length;
store->base.has_data_stored = flash_store_contiguous_blocks_has_data_stored;
store->base.get_max_data_length = flash_store_contiguous_blocks_get_max_data_length;
store->base.get_flash_size = flash_store_contiguous_blocks_get_flash_size;
store->base.get_num_blocks = flash_store_contiguous_blocks_get_num_blocks;
store->base_addr = base_addr;
store->decreasing = decreasing;
store->variable = variable;
store->flash = flash;
store->state = state;
return 0;
}
/**
* Initialize flash storage for contiguous blocks of data.
*
* @param store The flash storage to initialize.
* @param state The flash store state to initialize.
* @param flash The flash device used for storage.
* @param base_addr The address of the first storage block. This must be aligned to a minimum erase
* block.
* @param block_count The number of data blocks used for storage.
* @param data_length The minimum length of each data block.
* @param hash Optional hash engine to use for data validation. If a hash engine is provided, data
* integrity is checked when reading.
* @param decreasing Flag indicating if the storage grows down in the device address space.
* @param variable Flag indicating if the blocks contain variable length data.
*
* @return 0 if the flash storage was successfully initialized or an error code.
*/
static int flash_store_contiguous_blocks_init (struct flash_store_contiguous_blocks *store,
struct flash_store_contiguous_blocks_state *state, const struct flash *flash,
uint32_t base_addr, size_t block_count, size_t data_length, const struct hash_engine *hash,
bool decreasing, bool variable)
{
int status;
status = flash_store_contiguous_blocks_init_storage_common (store, state, flash, base_addr,
block_count, data_length, decreasing, variable);
if (status != 0) {
return status;
}
if (hash) {
store->base.write = flash_store_contiguous_blocks_write_with_hash;
store->base.read = flash_store_contiguous_blocks_read_with_hash;
store->hash = hash;
}
else {
store->base.write = flash_store_contiguous_blocks_write_no_hash;
store->base.read = flash_store_contiguous_blocks_read_no_hash;
}
status = flash_store_contiguous_blocks_init_state (store, block_count, data_length);
if (status != 0) {
return status;
}
return 0;
}
/**
* Initialize flash storage for fixed sized contiguous blocks of data.
*
* @param store The flash storage to initialize.
* @param state The flash store state to initialize.
* @param flash The flash device used for storage.
* @param base_addr The address of the first storage block. This must be aligned to a minimum erase
* block.
* @param block_count The number of data blocks used for storage.
* @param data_length The length of each data block.
* @param hash Optional hash engine to use for data validation. If a hash engine is provided, data
* integrity is checked when reading.
*
* @return 0 if the flash storage was successfully initialized or an error code.
*/
int flash_store_contiguous_blocks_init_fixed_storage (struct flash_store_contiguous_blocks *store,
struct flash_store_contiguous_blocks_state *state, const struct flash *flash,
uint32_t base_addr, size_t block_count, size_t data_length, const struct hash_engine *hash)
{
return flash_store_contiguous_blocks_init (store, state, flash, base_addr, block_count,
data_length, hash, false, false);
}
/**
* Initialize flash storage for fixed sized contiguous blocks of data. Blocks will be stored in
* addresses decreasing from the first block.
*
* @param store The flash storage to initialize.
* @param state The flash store state to initialize.
* @param flash The flash device used for storage.
* @param base_addr The address of the first storage block. This must be aligned to a minimum erase
* block.
* @param block_count The number of data blocks used for storage.
* @param data_length The length of each data block.
* @param hash Optional hash engine to use for data validation. If a hash engine is provided, data
* integrity is checked when reading.
*
* @return 0 if the flash storage was successfully initialized or an error code.
*/
int flash_store_contiguous_blocks_init_fixed_storage_decreasing (
struct flash_store_contiguous_blocks *store, struct flash_store_contiguous_blocks_state *state,
const struct flash *flash, uint32_t base_addr, size_t block_count, size_t data_length,
const struct hash_engine *hash)
{
return flash_store_contiguous_blocks_init (store, state, flash, base_addr, block_count,
data_length, hash, true, false);
}
/**
* Initialize flash storage for variable sized contiguous blocks of data.
*
* @param store The flash storage to initialize.
* @param state The flash store state to initialize.
* @param flash The flash device used for storage.
* @param base_addr The address of the first storage block. This must be aligned to a minimum erase
* block.
* @param block_count The number of data blocks used for storage.
* @param min_length The minimum length required for each data block. The actual length length will
* be determined by the flash sector size.
* @param hash Optional hash engine to use for data validation. If a hash engine is provided, data
* integrity is checked when reading.
*
* @return 0 if the flash storage was successfully initialized or an error code.
*/
int flash_store_contiguous_blocks_init_variable_storage (
struct flash_store_contiguous_blocks *store, struct flash_store_contiguous_blocks_state *state,
const struct flash *flash, uint32_t base_addr, size_t block_count, size_t min_length,
const struct hash_engine *hash)
{
return flash_store_contiguous_blocks_init (store, state, flash, base_addr, block_count,
min_length, hash, false, true);
}
/**
* Initialize flash storage for variable sized contiguous blocks of data. Blocks will be stored in
* addresses decreasing from the first block.
*
* @param store The flash storage to initialize.
* @param state The flash store state to initialize.
* @param flash The flash device used for storage.
* @param base_addr The address of the first storage block. This must be aligned to a minimum erase
* block.
* @param block_count The number of data blocks used for storage.
* @param min_length The minimum length required for each data block. The actual length length will
* be determined by the flash sector size.
* @param hash Optional hash engine to use for data validation. If a hash engine is provided, data
* integrity is checked when reading.
*
* @return 0 if the flash storage was successfully initialized or an error code.
*/
int flash_store_contiguous_blocks_init_variable_storage_decreasing (
struct flash_store_contiguous_blocks *store, struct flash_store_contiguous_blocks_state *state,
const struct flash *flash, uint32_t base_addr, size_t block_count, size_t min_length,
const struct hash_engine *hash)
{
return flash_store_contiguous_blocks_init (store, state, flash, base_addr, block_count,
min_length, hash, true, true);
}
/**
* Initialize only the variable state for flash store interface.
*
* @param store The flash storage to initialize.
* @param block_count The number of data blocks used for storage.
* @param data_length The minimum length of each data block.
*
* @return 0 if the flash storage was successfully initialized or an error code.
*/
int flash_store_contiguous_blocks_init_state (
const struct flash_store_contiguous_blocks *store, size_t block_count, size_t data_length)
{
int status;
if (store->hash) {
status = flash_store_contiguous_blocks_init_state_common (store, block_count, data_length,
SHA256_HASH_LENGTH);
}
else {
status = flash_store_contiguous_blocks_init_state_common (store, block_count, data_length,
0);
}
return status;
}
/**
* Release the resources used for flash block storage.
*
* @param store The flash storage to release.
*/
void flash_store_contiguous_blocks_release (const struct flash_store_contiguous_blocks *store)
{
#ifdef FLASH_STORE_SUPPORT_NO_PARTIAL_PAGE_WRITE
if (store) {
platform_free (store->state->page_buffer);
platform_mutex_free (&store->state->lock);
}
#else
UNUSED (store);
#endif
}
/**
* Configure the flash storage to write a backwards-compatible header on variable length data.
* This type of header is always accepted when reading variable length data.
*
* @param store The flash storage to configure.
*/
void flash_store_contiguous_blocks_use_length_only_header (
struct flash_store_contiguous_blocks *store)
{
if (store) {
store->state->old_header = true;
}
}