subsys: storage: Add flash_map abstraction module

Introduce flas_map module is abstraction over flash memory and its
driver for using flash memories along with description of
available flash areas.
Module provides simple API for write/read/erase and so one.

Signed-off-by: Andrzej Puzdrowski <andrzej.puzdrowski@nordicsemi.no>
This commit is contained in:
Andrzej Puzdrowski 2017-12-08 11:50:46 +01:00 committed by Anas Nashif
parent 4a713aa824
commit 25269fb0ed
13 changed files with 586 additions and 0 deletions

90
include/flash_map.h Normal file
View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2017 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __FLASH_MAP_H__
#define __FLASH_MAP_H__
#ifdef __cplusplus
extern "C" {
#endif
/**
*
* Provides abstraction of flash regions for type of use.
* I.e. dude where's my image?
*
* System will contain a map which contains flash areas. Every
* region will contain flash identifier, offset within flash and length.
*
* 1. This system map could be in a file within filesystem (Initializer
* must know/figure out where the filesystem is at).
* 2. Map could be at fixed location for project (compiled to code)
* 3. Map could be at specific place in flash (put in place at mfg time).
*
* Note that the map you use must be valid for BSP it's for,
* match the linker scripts when platform executes from flash,
* and match the target offset specified in download script.
*/
#include <zephyr/types.h>
#include <stddef.h>
#include <sys/types.h>
#define SOC_FLASH_0_ID 0 /* device_id for SoC flash memory driver */
#define SPI_FLASH_0_ID 1 /* device_id for external SPI flash driver */
struct flash_area {
u8_t fa_id;
u8_t fa_device_id;
u16_t pad16;
off_t fa_off;
size_t fa_size;
};
struct flash_sector {
off_t fs_off;
size_t fs_size;
};
/*
* Initializes flash map. Memory will be referenced by flash_map code
* from this on.
*/
void flash_map_init(void);
/*
* Start using flash area.
*/
int flash_area_open(u8_t id, const struct flash_area **fa);
void flash_area_close(const struct flash_area *fa);
/*
* Read/write/erase. Offset is relative from beginning of flash area.
*/
int flash_area_read(const struct flash_area *fa, off_t off, void *dst,
size_t len);
int flash_area_write(const struct flash_area *fa, off_t off, const void *src,
size_t len);
int flash_area_erase(const struct flash_area *fa, off_t off, size_t len);
/* int flash_area_is_empty(const struct flash_area *, bool *); */
/*
* Alignment restriction for flash writes.
*/
u8_t flash_area_align(const struct flash_area *fa);
/*
* Given flash area ID, return info about sectors within the area.
*/
int flash_area_get_sectors(int fa_id, uint32_t *count,
struct flash_sector *sectors);
#ifdef __cplusplus
}
#endif
#endif /* __FLASH_MAP_H__ */

View file

@ -10,3 +10,4 @@ add_subdirectory_ifdef(CONFIG_MCUBOOT_IMG_MANAGER dfu)
add_subdirectory_ifdef(CONFIG_NET_BUF net)
add_subdirectory_ifdef(CONFIG_USB usb)
add_subdirectory(random)
add_subdirectory(storage)

View file

@ -28,3 +28,5 @@ source "subsys/usb/Kconfig"
source "subsys/dfu/Kconfig"
source "subsys/random/Kconfig"
source "subsys/storage/Kconfig"

View file

@ -0,0 +1 @@
add_subdirectory_ifdef(CONFIG_FLASH_MAP flash_map)

13
subsys/storage/Kconfig Normal file
View file

@ -0,0 +1,13 @@
# Kconfig - Subsystem configuration options
#
# Copyright (c) 2018 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
menu "Storage"
source "subsys/storage/flash_map/Kconfig"
endmenu

View file

@ -0,0 +1,3 @@
zephyr_sources(flash_map.c)
zephyr_sources_ifndef(CONFIG_FLASH_MAP_CUSTOM flash_map_default.c)

View file

@ -0,0 +1,28 @@
# Kconfig - Flash map abstraction module
#
# Copyright (c) 2017 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
#
# Flash map
#
menuconfig FLASH_MAP
bool
prompt "Flash map abstraction module"
default n
depends on FLASH
help
Enable support of flash map abstraction.
config FLASH_MAP_CUSTOM
bool
prompt "Custom flash map description"
default n
depends on FLASH_MAP
help
This option enables custom flash map description.
User must provide such a description in place of default on
if had enabled this option.

View file

@ -0,0 +1,270 @@
/*
* Copyright (c) 2017 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <stddef.h>
#include <sys/types.h>
#include <device.h>
#include <flash_map.h>
#include <flash.h>
#include <soc.h>
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
struct layout_data {
u32_t area_idx;
u32_t area_off;
u32_t area_len;
void *ret; /* struct flash_area* or struct flash_sector* */
u32_t ret_idx;
u32_t ret_len;
int status;
};
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
struct driver_map_entry {
u8_t id;
const char * const name;
};
static const struct driver_map_entry flash_drivers_map[] = {
#ifdef FLASH_DRIVER_NAME /* SoC embedded flash driver */
{SOC_FLASH_0_ID, FLASH_DRIVER_NAME},
#endif
#ifdef CONFIG_SPI_FLASH_W25QXXDV
{SPI_FLASH_0_ID, CONFIG_SPI_FLASH_W25QXXDV_DRV_NAME},
#endif
};
const struct flash_area *flash_map;
extern const int flash_map_entries;
int flash_area_open(u8_t id, const struct flash_area **fap)
{
const struct flash_area *area;
int i;
if (flash_map == NULL) {
return -EACCES;
}
for (i = 0; i < flash_map_entries; i++) {
area = flash_map + i;
if (area->fa_id == id) {
*fap = area;
return 0;
}
}
return -ENOENT;
}
void flash_area_close(const struct flash_area *fa)
{
/* nothing to do for now */
}
static struct device *get_flah_dev_from_id(u8_t id)
{
for (int i = 0; i < ARRAY_SIZE(flash_drivers_map); i++) {
if (flash_drivers_map[i].id == id) {
return device_get_binding(flash_drivers_map[i].name);
}
}
k_panic();
}
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);
}
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;
}
#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, int *cnt, void *ret,
flash_page_cb cb, struct layout_data *cb_data)
{
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 = 0;
cb_data->ret_len = *cnt;
cb_data->status = 0;
flash_dev = get_flah_dev_from_id(fa->fa_device_id);
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)
{
struct device *dev;
if (!is_in_flash_area_bounds(fa, off, len)) {
return -1;
}
dev = get_flah_dev_from_id(fa->fa_device_id);
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)
{
struct device *flash_dev;
int rc;
if (!is_in_flash_area_bounds(fa, off, len)) {
return -1;
}
flash_dev = get_flah_dev_from_id(fa->fa_device_id);
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)
{
struct device *flash_dev;
int rc;
if (!is_in_flash_area_bounds(fa, off, len)) {
return -1;
}
flash_dev = get_flah_dev_from_id(fa->fa_device_id);
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;
}
u8_t flash_area_align(const struct flash_area *fa)
{
struct device *dev;
dev = get_flah_dev_from_id(fa->fa_device_id);
return flash_get_write_block_size(dev);
}
void flash_map_init(void)
{
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2017 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <flash_map.h>
const struct flash_area default_flash_map[] = {
/* FLASH_AREA_BOOTLOADER */
{
.fa_id = 0,
.fa_device_id = SOC_FLASH_0_ID,
.fa_off = FLASH_AREA_MCUBOOT_OFFSET,
.fa_size = FLASH_AREA_MCUBOOT_SIZE,
},
/* FLASH_AREA_IMAGE_0 */
{
.fa_id = 1,
.fa_device_id = SOC_FLASH_0_ID,
.fa_off = FLASH_AREA_IMAGE_0_OFFSET,
.fa_size = FLASH_AREA_IMAGE_0_SIZE,
},
/* FLASH_AREA_IMAGE_1 */
{
.fa_id = 2,
.fa_device_id = SOC_FLASH_0_ID,
.fa_off = FLASH_AREA_IMAGE_1_OFFSET,
.fa_size = FLASH_AREA_IMAGE_1_SIZE,
},
/* FLASH_AREA_IMAGE_SCRATCH */
{
.fa_id = 3,
.fa_device_id = SOC_FLASH_0_ID,
.fa_off = FLASH_AREA_IMAGE_SCRATCH_OFFSET,
.fa_size = FLASH_AREA_IMAGE_SCRATCH_SIZE,
},
#ifdef CONFIG_FILE_SYSTEM_NFFS
/* FLASH_AREA_NFFS_ */
{
.fa_id = 4,
.fa_device_id = SOC_FLASH_0_ID,
.fa_off = FLASH_AREA_NFFS_OFFSET,
.fa_size = FLASH_AREA_NFFS_SIZE,
},
#endif
};
const int flash_map_entries = ARRAY_SIZE(default_flash_map);
const struct flash_area *flash_map = default_flash_map;

View file

@ -0,0 +1,5 @@
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
project(NONE)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

View file

@ -0,0 +1,9 @@
CONFIG_ZTEST=y
CONFIG_STDOUT_CONSOLE=y
CONFIG_FLASH=y
CONFIG_SOC_FLASH_NRF5=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_ARM_CORE_MPU=n
CONFIG_ARM_MPU=n
CONFIG_ARM_MPU_NRF52X=n

View file

@ -0,0 +1,103 @@
/*
* Copyright (c) 2017 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ztest.h>
#include <flash.h>
#include <flash_map.h>
#define FLASH_AREA_BOOTLOADER 0
#define FLASH_AREA_IMAGE_0 1
#define FLASH_AREA_IMAGE_1 2
#define FLASH_AREA_IMAGE_SCRATCH 3
extern int flash_map_entries;
struct flash_sector fs_sectors[256];
/*
* Test flash_area_to_sectors()
*/
void flash_map_test_case_2(void)
{
const struct flash_area *fa;
int sec_cnt;
int i;
int rc;
off_t off;
u8_t wd[256];
u8_t rd[256];
struct device *flash_dev;
rc = flash_area_open(FLASH_AREA_IMAGE_1, &fa);
zassert_true(rc == 0, "flash_area_open() fail");
/* First erase the area so it's ready for use. */
flash_dev = device_get_binding(CONFIG_SOC_FLASH_NRF5_DEV_NAME);
rc = flash_erase(flash_dev, fa->fa_off, fa->fa_size);
zassert_true(rc == 0, "flash area erase fail");
memset(wd, 0xa5, sizeof(wd));
rc = flash_area_get_sectors(FLASH_AREA_IMAGE_1, &sec_cnt, fs_sectors);
zassert_true(rc == 0, "flash_area_get_sectors failed");
/* write stuff to beginning of every sector */
off = 0;
for (i = 0; i < sec_cnt; i++) {
rc = flash_area_write(fa, off, wd, sizeof(wd));
zassert_true(rc == 0, "flash_area_write() fail");
/* read it back via hal_flash_Read() */
rc = flash_read(flash_dev, fa->fa_off + off, rd, sizeof(rd));
zassert_true(rc == 0, "hal_flash_read() fail");
rc = memcmp(wd, rd, sizeof(wd));
zassert_true(rc == 0, "read data != write data");
(void) flash_write_protection_set(flash_dev, false);
/* write stuff to end of area */
rc = flash_write(flash_dev, fa->fa_off + off +
fs_sectors[i].fs_size - sizeof(wd),
wd, sizeof(wd));
zassert_true(rc == 0, "hal_flash_write() fail");
/* and read it back */
memset(rd, 0, sizeof(rd));
rc = flash_area_read(fa, off + fs_sectors[i].fs_size -
sizeof(rd),
rd, sizeof(rd));
zassert_true(rc == 0, "hal_flash_read() fail");
rc = memcmp(wd, rd, sizeof(rd));
zassert_true(rc == 0, "read data != write data");
off += fs_sectors[i].fs_size;
}
/* erase it */
rc = flash_area_erase(fa, 0, fa->fa_size);
zassert_true(rc == 0, "read data != write data");
/* should read back ff all throughout*/
memset(wd, 0xff, sizeof(wd));
for (off = 0; off < fa->fa_size; off += sizeof(rd)) {
rc = flash_area_read(fa, off, rd, sizeof(rd));
zassert_true(rc == 0, "hal_flash_read() fail");
rc = memcmp(wd, rd, sizeof(rd));
zassert_true(rc == 0, "area not erased");
}
}
void test_main(void *p1, void *p2, void *p3)
{
ztest_test_suite(test_flash_map,
ztest_unit_test(flash_map_test_case_2));
ztest_run_test_suite(test_flash_map);
}

View file

@ -0,0 +1,5 @@
tests:
test:
build_only: true
platform_whitelist: nrf52840_pca10056, nrf52_pca10040, nrf51_pca10028
tags: flash_map