zephyr/subsys/storage/flash_map/flash_map.c
Andrzej Puzdrowski 053857e09a storage/flash_map: Added function for get erased byte value
Added flash_area_erased_val() function for get value of erased
byte of memory which is under flash area.
This function already exist in MCUBoot and zephyr dfu subsystem
which makes simultaneous usage of both impossible.

Signed-off-by: Andrzej Puzdrowski <andrzej.puzdrowski@nordicsemi.no>
2020-10-30 15:53:38 +01:00

338 lines
7.3 KiB
C

/*
* Copyright (c) 2017 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
* Copyright (c) 2017 Linaro Ltd
* Copyright (c) 2020 Gerson Fernando Budke <nandojve@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <stddef.h>
#include <sys/types.h>
#include <device.h>
#include <storage/flash_map.h>
#include <drivers/flash.h>
#include <soc.h>
#include <init.h>
#if defined(CONFIG_FLASH_AREA_CHECK_INTEGRITY)
#include <tinycrypt/constants.h>
#include <tinycrypt/sha256.h>
#include <string.h>
#endif
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
struct layout_data {
uint32_t area_idx;
uint32_t area_off;
uint32_t area_len;
void *ret; /* struct flash_area* or struct flash_sector* */
uint32_t ret_idx;
uint32_t ret_len;
int status;
};
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
extern const struct flash_area *flash_map;
extern const int flash_map_entries;
static struct flash_area const *get_flash_area_from_id(int idx)
{
for (int i = 0; i < flash_map_entries; i++) {
if (flash_map[i].fa_id == idx) {
return &flash_map[i];
}
}
return NULL;
}
void flash_area_foreach(flash_area_cb_t user_cb, void *user_data)
{
for (int i = 0; i < flash_map_entries; i++) {
user_cb(&flash_map[i], user_data);
}
}
int flash_area_open(uint8_t id, const struct flash_area **fap)
{
const struct flash_area *area;
if (flash_map == NULL) {
return -EACCES;
}
area = get_flash_area_from_id(id);
if (area == NULL) {
return -ENOENT;
}
*fap = area;
return 0;
}
void flash_area_close(const struct flash_area *fa)
{
/* nothing to do for now */
}
static inline bool is_in_flash_area_bounds(const struct flash_area *fa,
off_t off, size_t len)
{
return (off <= fa->fa_size && off + len <= fa->fa_size);
}
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
/*
* Check if a flash_page_foreach() callback should exit early, due to
* one of the following conditions:
*
* - The flash page described by "info" is before the area of interest
* described in "data"
* - The flash page is after the end of the area
* - There are too many flash pages on the device to fit in the array
* held in data->ret. In this case, data->status is set to -ENOMEM.
*
* The value to return to flash_page_foreach() is stored in
* "bail_value" if the callback should exit early.
*/
static bool should_bail(const struct flash_pages_info *info,
struct layout_data *data,
bool *bail_value)
{
if (info->start_offset < data->area_off) {
*bail_value = true;
return true;
} else if (info->start_offset >= data->area_off + data->area_len) {
*bail_value = false;
return true;
} else if (data->ret_idx >= data->ret_len) {
data->status = -ENOMEM;
*bail_value = false;
return true;
}
return false;
}
/*
* Generic page layout discovery routine. This is kept separate to
* support both the deprecated flash_area_to_sectors() and the current
* flash_area_get_sectors(). A lot of this can be inlined once
* flash_area_to_sectors() is removed.
*/
static int flash_area_layout(int idx, uint32_t *cnt, void *ret,
flash_page_cb cb, struct layout_data *cb_data)
{
const struct device *flash_dev;
cb_data->area_idx = idx;
const struct flash_area *fa;
fa = get_flash_area_from_id(idx);
if (fa == NULL) {
return -EINVAL;
}
cb_data->area_idx = idx;
cb_data->area_off = fa->fa_off;
cb_data->area_len = fa->fa_size;
cb_data->ret = ret;
cb_data->ret_idx = 0U;
cb_data->ret_len = *cnt;
cb_data->status = 0;
flash_dev = device_get_binding(fa->fa_dev_name);
if (flash_dev == NULL) {
return -ENODEV;
}
flash_page_foreach(flash_dev, cb, cb_data);
if (cb_data->status == 0) {
*cnt = cb_data->ret_idx;
}
return cb_data->status;
}
static bool get_sectors_cb(const struct flash_pages_info *info, void *datav)
{
struct layout_data *data = datav;
struct flash_sector *ret = data->ret;
bool bail;
if (should_bail(info, data, &bail)) {
return bail;
}
ret[data->ret_idx].fs_off = info->start_offset - data->area_off;
ret[data->ret_idx].fs_size = info->size;
data->ret_idx++;
return true;
}
int flash_area_get_sectors(int idx, uint32_t *cnt, struct flash_sector *ret)
{
struct layout_data data;
return flash_area_layout(idx, cnt, ret, get_sectors_cb, &data);
}
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
int flash_area_read(const struct flash_area *fa, off_t off, void *dst,
size_t len)
{
const struct device *dev;
if (!is_in_flash_area_bounds(fa, off, len)) {
return -EINVAL;
}
dev = device_get_binding(fa->fa_dev_name);
return flash_read(dev, fa->fa_off + off, dst, len);
}
int flash_area_write(const struct flash_area *fa, off_t off, const void *src,
size_t len)
{
const struct device *flash_dev;
int rc;
if (!is_in_flash_area_bounds(fa, off, len)) {
return -EINVAL;
}
flash_dev = device_get_binding(fa->fa_dev_name);
rc = flash_write_protection_set(flash_dev, false);
if (rc) {
return rc;
}
rc = flash_write(flash_dev, fa->fa_off + off, (void *)src, len);
/* Ignore errors here - this does not affect write operation */
(void) flash_write_protection_set(flash_dev, true);
return rc;
}
int flash_area_erase(const struct flash_area *fa, off_t off, size_t len)
{
const struct device *flash_dev;
int rc;
if (!is_in_flash_area_bounds(fa, off, len)) {
return -EINVAL;
}
flash_dev = device_get_binding(fa->fa_dev_name);
rc = flash_write_protection_set(flash_dev, false);
if (rc) {
return rc;
}
rc = flash_erase(flash_dev, fa->fa_off + off, len);
/* Ignore errors here - this does not affect write operation */
(void) flash_write_protection_set(flash_dev, true);
return rc;
}
uint8_t flash_area_align(const struct flash_area *fa)
{
const struct device *dev;
dev = device_get_binding(fa->fa_dev_name);
return flash_get_write_block_size(dev);
}
int flash_area_has_driver(const struct flash_area *fa)
{
if (device_get_binding(fa->fa_dev_name) == NULL) {
return -ENODEV;
}
return 1;
}
const struct device *flash_area_get_device(const struct flash_area *fa)
{
return device_get_binding(fa->fa_dev_name);
}
uint8_t flash_area_erased_val(const struct flash_area *fa)
{
const struct flash_parameters *param;
param = flash_get_parameters(device_get_binding(fa->fa_dev_name));
return param->erase_value;
}
#if defined(CONFIG_FLASH_AREA_CHECK_INTEGRITY)
int flash_area_check_int_sha256(const struct flash_area *fa,
const struct flash_area_check *fac)
{
unsigned char hash[TC_SHA256_DIGEST_SIZE];
struct tc_sha256_state_struct sha;
const struct device *dev;
int to_read;
int pos;
int rc;
if (fa == NULL || fac == NULL || fac->match == NULL ||
fac->rbuf == NULL || fac->clen == 0 || fac->rblen == 0) {
return -EINVAL;
}
if (!is_in_flash_area_bounds(fa, fac->off, fac->clen)) {
return -EINVAL;
}
if (tc_sha256_init(&sha) != TC_CRYPTO_SUCCESS) {
return -ESRCH;
}
dev = device_get_binding(fa->fa_dev_name);
to_read = fac->rblen;
for (pos = 0; pos < fac->clen; pos += to_read) {
if (pos + to_read > fac->clen) {
to_read = fac->clen - pos;
}
rc = flash_read(dev, (fa->fa_off + fac->off + pos),
fac->rbuf, to_read);
if (rc != 0) {
return rc;
}
if (tc_sha256_update(&sha,
fac->rbuf,
to_read) != TC_CRYPTO_SUCCESS) {
return -ESRCH;
}
}
if (tc_sha256_final(hash, &sha) != TC_CRYPTO_SUCCESS) {
return -ESRCH;
}
if (memcmp(hash, fac->match, TC_SHA256_DIGEST_SIZE)) {
return -EILSEQ;
}
return 0;
}
#endif /* CONFIG_FLASH_AREA_CHECK_INTEGRITY */