diff --git a/include/flash_map.h b/include/flash_map.h new file mode 100644 index 0000000000..f11cd40f6b --- /dev/null +++ b/include/flash_map.h @@ -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 +#include +#include + +#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__ */ diff --git a/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index ebe34b624a..f04039e3fa 100644 --- a/subsys/CMakeLists.txt +++ b/subsys/CMakeLists.txt @@ -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) diff --git a/subsys/Kconfig b/subsys/Kconfig index 79874dca64..39e389838f 100644 --- a/subsys/Kconfig +++ b/subsys/Kconfig @@ -28,3 +28,5 @@ source "subsys/usb/Kconfig" source "subsys/dfu/Kconfig" source "subsys/random/Kconfig" + +source "subsys/storage/Kconfig" diff --git a/subsys/storage/CMakeLists.txt b/subsys/storage/CMakeLists.txt new file mode 100644 index 0000000000..e19f3a9d01 --- /dev/null +++ b/subsys/storage/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory_ifdef(CONFIG_FLASH_MAP flash_map) diff --git a/subsys/storage/Kconfig b/subsys/storage/Kconfig new file mode 100644 index 0000000000..583339f83e --- /dev/null +++ b/subsys/storage/Kconfig @@ -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 diff --git a/subsys/storage/flash_map/CMakeLists.txt b/subsys/storage/flash_map/CMakeLists.txt new file mode 100644 index 0000000000..e9f48b555b --- /dev/null +++ b/subsys/storage/flash_map/CMakeLists.txt @@ -0,0 +1,3 @@ +zephyr_sources(flash_map.c) +zephyr_sources_ifndef(CONFIG_FLASH_MAP_CUSTOM flash_map_default.c) + diff --git a/subsys/storage/flash_map/Kconfig b/subsys/storage/flash_map/Kconfig new file mode 100644 index 0000000000..0fba6d0e48 --- /dev/null +++ b/subsys/storage/flash_map/Kconfig @@ -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. diff --git a/subsys/storage/flash_map/flash_map.c b/subsys/storage/flash_map/flash_map.c new file mode 100644 index 0000000000..063640a932 --- /dev/null +++ b/subsys/storage/flash_map/flash_map.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#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) +{ +} diff --git a/subsys/storage/flash_map/flash_map_default.c b/subsys/storage/flash_map/flash_map_default.c new file mode 100644 index 0000000000..5bea34e02e --- /dev/null +++ b/subsys/storage/flash_map/flash_map_default.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +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; diff --git a/tests/subsys/storage/flash_mpa/CMakeLists.txt b/tests/subsys/storage/flash_mpa/CMakeLists.txt new file mode 100644 index 0000000000..92a4288bc1 --- /dev/null +++ b/tests/subsys/storage/flash_mpa/CMakeLists.txt @@ -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}) diff --git a/tests/subsys/storage/flash_mpa/prj.conf b/tests/subsys/storage/flash_mpa/prj.conf new file mode 100644 index 0000000000..42f41af684 --- /dev/null +++ b/tests/subsys/storage/flash_mpa/prj.conf @@ -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 diff --git a/tests/subsys/storage/flash_mpa/src/main.c b/tests/subsys/storage/flash_mpa/src/main.c new file mode 100644 index 0000000000..fdad78273f --- /dev/null +++ b/tests/subsys/storage/flash_mpa/src/main.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + + + +#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); +} diff --git a/tests/subsys/storage/flash_mpa/testcase.yaml b/tests/subsys/storage/flash_mpa/testcase.yaml new file mode 100644 index 0000000000..9a836b182f --- /dev/null +++ b/tests/subsys/storage/flash_mpa/testcase.yaml @@ -0,0 +1,5 @@ +tests: + test: + build_only: true + platform_whitelist: nrf52840_pca10056, nrf52_pca10040, nrf51_pca10028 + tags: flash_map