This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-nuttx-apps.git
The following commit(s) were added to refs/heads/master by this push: new c427f408c testing:add MTD Fail-safe config-data tests c427f408c is described below commit c427f408cbb770662d253a334f36bab9e0c3d61d Author: 田昕 <tianx...@xiaomi.com> AuthorDate: Mon Aug 22 10:20:06 2022 +0800 testing:add MTD Fail-safe config-data tests Signed-off-by: 田昕 <tianx...@xiaomi.com> --- testing/mtd_config_fs/Kconfig | 50 + testing/mtd_config_fs/Make.defs | 23 + testing/mtd_config_fs/Makefile | 34 + testing/mtd_config_fs/README.md | 16 + testing/mtd_config_fs/mtd_config_fs_test_main.c | 2039 +++++++++++++++++++++++ 5 files changed, 2162 insertions(+) diff --git a/testing/mtd_config_fs/Kconfig b/testing/mtd_config_fs/Kconfig new file mode 100644 index 000000000..443f8bf67 --- /dev/null +++ b/testing/mtd_config_fs/Kconfig @@ -0,0 +1,50 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config TESTING_MTD_CONFIG_FAIL_SAFE + tristate "MTD Config fail-safe storage test" + default n + depends on MTD_CONFIG_FAIL_SAFE + ---help--- + Enable the fail-safe storage test + +if TESTING_MTD_CONFIG_FAIL_SAFE + +config TESTING_MTD_CONFIG_FAIL_SAFE_PROGNAME + string "Program name" + default "mtdconfig_fs_test" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME + string "MTD driver mount pt" + default "/dev/mtdnvs_flash" + +config TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_MAXNAME + int "MTD driver mount pt path len" + default 64 + +config TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE + int "MTD flash section size" + default 4096 + +config TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_COUNT + int "MTD flash section count" + default 3 + +config TESTING_MTD_CONFIG_FAIL_SAFE_PRIORITY + int "MTD nvs test task priority" + default 100 + +config TESTING_MTD_CONFIG_FAIL_SAFE_STACKSIZE + int "MTD nvs test stack size" + default 4096 + +config TESTING_MTD_CONFIG_FAIL_SAFE_VERBOSE + bool "Verbose output" + default n + +endif diff --git a/testing/mtd_config_fs/Make.defs b/testing/mtd_config_fs/Make.defs new file mode 100644 index 000000000..7d8926c18 --- /dev/null +++ b/testing/mtd_config_fs/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/testing/mtd_config_fs/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE),) +CONFIGURED_APPS += $(APPDIR)/testing/mtd_config_fs +endif diff --git a/testing/mtd_config_fs/Makefile b/testing/mtd_config_fs/Makefile new file mode 100644 index 000000000..7446e60ae --- /dev/null +++ b/testing/mtd_config_fs/Makefile @@ -0,0 +1,34 @@ +############################################################################ +# apps/testing/mtd_nvs/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# MTD nvs test application info + +PROGNAME = $(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_PROGNAME) +PRIORITY = $(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_PRIORITY) +STACKSIZE = $(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_STACKSIZE) +MODULE = $(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE) + +# MTD nvs test + +MAINSRC = mtd_config_fs_test_main.c + +include $(APPDIR)/Application.mk diff --git a/testing/mtd_config_fs/README.md b/testing/mtd_config_fs/README.md new file mode 100644 index 000000000..7674e5f2c --- /dev/null +++ b/testing/mtd_config_fs/README.md @@ -0,0 +1,16 @@ +# Testing / `mtd_nvs` MTD non-volatile storage Test + +This is a test for MTD non-volatile storage. MTD non-volatile storage was originally +implemented in Zephyr by Laczen. We made several modification to the original design. +The main purpose of those modification was: +1. support C-string key in nvs API(Original design only support uint16_t as key) +2. Meanwhile achieve better performance by limiting flash read times(Theoratically +better than Zephyr subsys/settings, which is based on original NVS). + + +- `CONFIG_TESTING_FAILSAFE_MTD_CONFIG` – Enable the test. +- `CONFIG_TESTING_FAILSAFE_MTD_CONFIG_VERBOSE` – Verbose output. + +EXAMPLE + mtdconfig_fs_test -m /dev/config – Test MTD NVS on /dev/config + mtdconfig_fs_test -h – Get help message diff --git a/testing/mtd_config_fs/mtd_config_fs_test_main.c b/testing/mtd_config_fs/mtd_config_fs_test_main.c new file mode 100644 index 000000000..782743087 --- /dev/null +++ b/testing/mtd_config_fs/mtd_config_fs_test_main.c @@ -0,0 +1,2039 @@ +/**************************************************************************** + * apps/testing/mtd_config_fs/mtd_config_fs_test_main.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/mtd/mtd.h> +#include <nuttx/mtd/configdata.h> + +#include <sys/mount.h> +#include <sys/ioctl.h> +#include <sys/statfs.h> + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <malloc.h> +#include <unistd.h> +#include <fcntl.h> +#include <dirent.h> +#include <string.h> +#include <errno.h> +#include <nuttx/crc8.h> +#include <debug.h> +#include <assert.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#define TEST_KEY1 "testkey1" +#define TEST_KEY2 "testkey2" +#define TEST_DATA1 "abc" +#define TEST_DATA2 "def" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Allocation Table Entry */ + +begin_packed_struct struct nvs_ate +{ + uint32_t id; /* data id */ + uint16_t offset; /* data offset within sector */ + uint16_t len; /* data len within sector */ + uint16_t key_len; /* key string len */ + uint8_t part; /* part of a multipart data - future extension */ + uint8_t crc8; /* crc8 check of the ate entry */ + uint8_t expired; /* 0xFF-newest entry, others-old entry */ + uint8_t reserved[3]; /* for future extension */ +} end_packed_struct; + +/* Pre-allocated simulated flash */ + +struct mtdnvs_ctx_s +{ + char mountdir[CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_MAXNAME]; + struct mallinfo mmbefore; + struct mallinfo mmprevious; + struct mallinfo mmafter; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: fnv_hash_32 + ****************************************************************************/ + +static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len) +{ + uint32_t i = 0; + uint32_t hval = 2166136261; + + /* FNV-1 hash each octet in the buffer */ + + while (i++ < len) + { + /* multiply by the 32 bit FNV magic prime mod 2^32 */ + + hval *= 0x01000193; + + /* xor the bottom with the current octet */ + + hval ^= *input++; + } + + return hval; +} + +/**************************************************************************** + * Name: fill_crc8_update + ****************************************************************************/ + +static void fill_crc8_update(FAR struct nvs_ate *entry) +{ + uint8_t ate_crc; + + ate_crc = crc8part((FAR const uint8_t *)entry, + offsetof(struct nvs_ate, crc8), 0xff); + entry->crc8 = ate_crc; +} + +/**************************************************************************** + * Name: fill_gc_done_ate + ****************************************************************************/ + +static void fill_corrupted_close_ate(FAR struct nvs_ate *close_ate) +{ + memset((FAR void *)close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE, + sizeof(struct nvs_ate)); + close_ate->id = 0xffffffff; + close_ate->len = 0U; +} + +/**************************************************************************** + * Name: fill_close_ate + ****************************************************************************/ + +static void fill_close_ate(FAR struct nvs_ate *close_ate, int offset) +{ + memset((FAR void *)close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE, + sizeof(struct nvs_ate)); + close_ate->id = 0xffffffff; + close_ate->len = 0U; + close_ate->offset = offset; + fill_crc8_update(close_ate); +} + +/**************************************************************************** + * Name: fill_gc_done_ate + ****************************************************************************/ + +static void fill_gc_done_ate(FAR struct nvs_ate *gc_done_ate) +{ + memset((FAR void *)gc_done_ate, CONFIG_MTD_CONFIG_ERASEDVALUE, + sizeof(struct nvs_ate)); + gc_done_ate->id = 0xffffffff; + gc_done_ate->len = 0U; + fill_crc8_update(gc_done_ate); +} + +/**************************************************************************** + * Name: fill_ate + ****************************************************************************/ + +static void fill_ate(FAR struct nvs_ate *ate, FAR const char *key, + uint16_t len, uint16_t offset, bool expired) +{ + memset((FAR void *)ate, CONFIG_MTD_CONFIG_ERASEDVALUE, + sizeof(struct nvs_ate)); + ate->id = nvs_fnv_hash((FAR const uint8_t *)key, strlen(key) + 1) + % 0xfffffffd + 1; + ate->len = len; + ate->offset = offset; + ate->key_len = strlen(key) + 1; + fill_crc8_update(ate); + ate->expired = expired ? 0x7f : 0xff; +} + +/**************************************************************************** + * Name: fill_corrupted_ate + ****************************************************************************/ + +static void fill_corrupted_ate(FAR struct nvs_ate *ate, + FAR const char *key, uint16_t len, uint16_t offset) +{ + memset((FAR void *)ate, CONFIG_MTD_CONFIG_ERASEDVALUE, + sizeof(struct nvs_ate)); + ate->id = nvs_fnv_hash((FAR const uint8_t *)key, strlen(key) + 1) + % 0xfffffffd + 1; + ate->len = len; + ate->offset = offset; + ate->key_len = strlen(key) + 1; +} + +/**************************************************************************** + * Name: mtdnvs_showmemusage + ****************************************************************************/ + +static void mtdnvs_showmemusage(struct mallinfo *mmbefore, + struct mallinfo *mmafter) +{ + printf("VARIABLE BEFORE AFTER DELTA\n"); + printf("======== ======== ======== ========\n"); + printf("arena %8x %8x %8x\n", mmbefore->arena , mmafter->arena, + mmafter->arena - mmbefore->arena); + printf("ordblks %8d %8d %8d\n", mmbefore->ordblks , mmafter->ordblks, + mmafter->ordblks - mmbefore->ordblks); + printf("mxordblk %8x %8x %8x\n", mmbefore->mxordblk, mmafter->mxordblk, + mmafter->mxordblk - mmbefore->mxordblk); + printf("uordblks %8x %8x %8x\n", mmbefore->uordblks, mmafter->uordblks, + mmafter->uordblks - mmbefore->uordblks); + printf("fordblks %8x %8x %8x\n", mmbefore->fordblks, mmafter->fordblks, + mmafter->fordblks - mmbefore->fordblks); +} + +/**************************************************************************** + * Name: mtdnvs_endmemusage + ****************************************************************************/ + +static void mtdnvs_endmemusage(FAR struct mtdnvs_ctx_s *ctx) +{ + ctx->mmafter = mallinfo(); + + printf("\nFinal memory usage:\n"); + mtdnvs_showmemusage(&ctx->mmbefore, &ctx->mmafter); +} + +/**************************************************************************** + * Name: show_useage + ****************************************************************************/ + +static void show_useage(void) +{ + printf("Usage : mtdconfig_fs_test [OPTION [ARG]] ...\n"); + printf("-h show this help statement\n"); + printf("-m mount point to be tested e.g. [%s]\n", + CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME); +} + +/**************************************************************************** + * Name: teardown + ****************************************************************************/ + +static int teardown(void) +{ + int fd; + int ret; + + fd = open("/dev/config", 0); + if (fd < 0) + { + printf("%s:open failed, ret=%d\n", __func__, fd); + return -errno; + } + + ret = ioctl(fd, MTDIOC_BULKERASE, NULL); + if (ret < 0) + { + printf("%s:clear failed, ret=%d\n", __func__, ret); + return -errno; + } + + close(fd); + + ret = mtdconfig_unregister(); + if (ret < 0) + { + printf("%s:mtdconfig_unregister failed, ret=%d\n", __func__, ret); + return ret; + } + + return ret; +} + +extern int find_mtddriver(FAR const char *pathname, + FAR struct inode **ppinode); + +/**************************************************************************** + * Name: setup + ****************************************************************************/ + +static int setup(struct mtdnvs_ctx_s *ctx) +{ + int ret; + FAR struct inode *sys_node; + + ret = find_mtddriver(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, + &sys_node); + if (ret < 0) + { + printf("ERROR: open %s failed: %d\n", + CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, ret); + return -errno; + } + + ret = mtdconfig_register(sys_node->u.i_mtd); + if (ret < 0) + { + printf("%s:mtdnvs_register failed, ret=%d\n", __func__, ret); + return ret; + } + + return ret; +} + +/**************************************************************************** + * Name: test_nvs_mount + ****************************************************************************/ + +static void test_nvs_mount(struct mtdnvs_ctx_s *ctx) +{ + int ret; + + printf("%s: test begin\n", __func__); + + ret = setup(ctx); + if (ret < 0) + { + printf("%s:mtdconfig_register failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* at the end of test, erase all blocks */ + + ret = teardown(); + if (ret < 0) + { + printf("%s:teardown failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + printf("%s: success\n", __func__); + return; + +test_fail: + + printf("%s: failed\n", __func__); + + return; +} + +/**************************************************************************** + * Name: execute_long_pattern_write + ****************************************************************************/ + +static int execute_long_pattern_write(const char *key) +{ + struct config_data_s data; + int i; + int fd; + int ret; + uint8_t rd_buf[512]; + uint8_t wr_buf[512]; + char pattern[4]; + + pattern[0] = 0xde; + pattern[1] = 0xad; + pattern[2] = 0xbe; + pattern[3] = 0xef; + + fd = open("/dev/config", 0); + if (fd < 0) + { + printf("%s:open failed, ret=%d\n", __func__, fd); + return -errno; + } + + strlcpy(data.name, key, sizeof(data.name)); + data.configdata = rd_buf; + data.len = sizeof(rd_buf); + ret = ioctl(fd, CFGDIOC_GETCONFIG, &data); + if (ret != -1 || errno != ENOENT) + { + printf("%s:CFGDIOC_GETCONFIG unexpected failure: %d\n", + __func__, ret); + goto err_fd; + } + + for (i = 0; i < sizeof(wr_buf); i += sizeof(pattern)) + { + memcpy(wr_buf + i, pattern, sizeof(pattern)); + } + + data.configdata = wr_buf; + data.len = sizeof(wr_buf); + + ret = ioctl(fd, CFGDIOC_SETCONFIG, &data); + if (ret != 0) + { + printf("%s:CFGDIOC_SETCONFIG failed, ret=%d\n", __func__, ret); + ret = -EIO; + goto err_fd; + } + + data.configdata = rd_buf; + data.len = sizeof(rd_buf); + ret = ioctl(fd, CFGDIOC_GETCONFIG, &data); + if (ret != 0) + { + printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret); + ret = -EIO; + goto err_fd; + } + + if (memcmp(wr_buf, rd_buf, sizeof(rd_buf)) != 0) + { + printf("%s:RD buff should be equal to the WR buff\n", __func__); + ret = -EIO; + goto err_fd; + } + +err_fd: + close(fd); + + return ret; +} + +/**************************************************************************** + * Name: test_nvs_write + ****************************************************************************/ + +static void test_nvs_write(struct mtdnvs_ctx_s *ctx) +{ + int ret; + + printf("%s: test begin\n", __func__); + + ret = setup(ctx); + if (ret < 0) + { + printf("%s:mtdconfig_register failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = execute_long_pattern_write(TEST_KEY1); + if (ret < 0) + { + printf("%s:execute_long_pattern_write failed, ret=%d\n", + __func__, ret); + goto test_fail; + } + + /* at the end of test, erase all blocks */ + + ret = teardown(); + if (ret < 0) + { + printf("%s:teardown failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + printf("%s: success\n", __func__); + return; + +test_fail: + + printf("%s: failed\n", __func__); + + return; +} + +/**************************************************************************** + * Name: test_nvs_corrupt_expire + * Description: Test that startup correctly handles invalid expire. + ****************************************************************************/ + +static void test_nvs_corrupt_expire(struct mtdnvs_ctx_s *ctx) +{ + struct nvs_ate ate; + int mtd_fd = -1; + int nvs_fd = -1; + int ret; + int i; + uint8_t erase_value = CONFIG_MTD_CONFIG_ERASEDVALUE; + struct config_data_s data; + char rd_buf[50]; + + printf("%s: test begin\n", __func__); + + mtd_fd = open(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, O_RDWR); + if (mtd_fd < 0) + { + printf("%s:mtdnvs_register failed, ret=%d\n", __func__, mtd_fd); + goto test_fail; + } + + /* write valid data */ + + ret = write(mtd_fd, TEST_KEY1, strlen(TEST_KEY1) + 1); + if (ret < 0) + { + printf("%s:write key1 failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = write(mtd_fd, TEST_DATA1, strlen(TEST_DATA1) + 1); + if (ret < 0) + { + printf("%s:write data1 failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* write valid data again, simulate overwrite data */ + + ret = write(mtd_fd, TEST_KEY1, strlen(TEST_KEY1) + 1); + if (ret < 0) + { + printf("%s:write key1 failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = write(mtd_fd, TEST_DATA2, strlen(TEST_DATA2) + 1); + if (ret < 0) + { + printf("%s:write data2 failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* set unused flash to 0xff */ + + for (i = 2 * (strlen(TEST_KEY1) + strlen(TEST_DATA1) + 2); + i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 4 * 16; + i++) + { + ret = write(mtd_fd, &erase_value, sizeof(erase_value)); + if (ret != sizeof(erase_value)) + { + printf("%s:erase failed, ret=%d\n", __func__, ret); + goto test_fail; + } + } + + /* Write ate */ + + fill_ate(&ate, TEST_KEY1, strlen(TEST_DATA2) + 1, + strlen(TEST_KEY1) + strlen(TEST_DATA1) + 2, false); + ret = write(mtd_fd, &ate, sizeof(ate)); + if (ret != sizeof(ate)) + { + printf("%s:write ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + fill_ate(&ate, TEST_KEY1, strlen(TEST_DATA1) + 1, 0, false); + ret = write(mtd_fd, &ate, sizeof(ate)); + if (ret != sizeof(ate)) + { + printf("%s:write ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* write gc_done ate */ + + fill_gc_done_ate(&ate); + ret = write(mtd_fd, &ate, sizeof(ate)); + if (ret != sizeof(ate)) + { + printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* write close ate, mark section 0 as closed */ + + fill_close_ate(&ate, + CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 4 * 16); + ret = write(mtd_fd, &ate, sizeof(ate)); + if (ret != sizeof(ate)) + { + printf("%s:write close ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + close(mtd_fd); + mtd_fd = -1; + + ret = setup(ctx); + if (ret < 0) + { + printf("%s:setup failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + nvs_fd = open("/dev/config", 0); + if (nvs_fd < 0) + { + printf("%s:open failed, ret=%d\n", __func__, nvs_fd); + goto test_fail; + } + + strlcpy(data.name, TEST_KEY1, sizeof(data.name)); + data.configdata = (FAR uint8_t *)rd_buf; + data.len = sizeof(rd_buf); + + ret = ioctl(nvs_fd, CFGDIOC_FIRSTCONFIG, &data); + if (ret != 0) + { + printf("%s:CFGDIOC_FIRSTCONFIG unexpected failure: %d\n", + __func__, ret); + goto test_fail; + } + + if (strncmp(rd_buf, TEST_DATA2, sizeof(rd_buf)) != 0) + { + printf("%s:unexpected value\n", __func__); + goto test_fail; + } + + ret = ioctl(nvs_fd, CFGDIOC_NEXTCONFIG, &data); + if (ret != -1 || errno != ENOENT) + { + printf("%s:CFGDIOC_NEXTCONFIG should return ENOENT, but: %d\n", + __func__, ret); + goto test_fail; + } + + close(nvs_fd); + nvs_fd = -1; + + /* at the end of test, erase all blocks */ + + ret = teardown(); + if (ret < 0) + { + printf("%s:teardown failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + printf("%s: success\n", __func__); + return; + +test_fail: + + if (mtd_fd >= 0) + close(mtd_fd); + + if (nvs_fd >= 0) + close(nvs_fd); + + printf("%s: failed\n", __func__); + + return; +} + +/**************************************************************************** + * Name: test_nvs_corrupted_write + ****************************************************************************/ + +static void test_nvs_corrupted_write(struct mtdnvs_ctx_s *ctx) +{ + int ret; + char rd_buf[512]; + char wr_buf_1[] = TEST_DATA1; + char wr_buf_2[] = TEST_DATA2; + char key1[] = TEST_KEY1; + int mtd_fd = -1; + int nvs_fd = -1; + int i; + uint8_t erase_value = CONFIG_MTD_CONFIG_ERASEDVALUE; + struct nvs_ate ate; + struct config_data_s data; + + printf("%s: test begin\n", __func__); + + mtd_fd = open(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, O_RDWR); + if (mtd_fd < 0) + { + printf("%s:mtdnvs_register failed, ret=%d\n", __func__, mtd_fd); + goto test_fail; + } + + /* lets simulate loss of part of data */ + + /* write valid data */ + + ret = write(mtd_fd, key1, sizeof(key1)); + if (ret != sizeof(key1)) + { + printf("%s:write key1 failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = write(mtd_fd, wr_buf_1, sizeof(wr_buf_1)); + if (ret != sizeof(wr_buf_1)) + { + printf("%s:write data1 failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* power loss occurs after we write data */ + + ret = write(mtd_fd, key1, sizeof(key1)); + if (ret != sizeof(key1)) + { + printf("%s:write key1 failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = write(mtd_fd, wr_buf_2, sizeof(wr_buf_1)); + if (ret != sizeof(wr_buf_2)) + { + printf("%s:write data2 failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* set unused flash to 0xff */ + + for (i = 2 * (sizeof(key1) + sizeof(wr_buf_1)); + i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 3 * 16; + i++) + { + ret = write(mtd_fd, &erase_value, sizeof(erase_value)); + if (ret != sizeof(erase_value)) + { + printf("%s:erase failed, ret=%d\n", __func__, ret); + goto test_fail; + } + } + + /* write ate */ + + fill_ate(&ate, key1, sizeof(wr_buf_1), 0, false); + ret = write(mtd_fd, &ate, sizeof(ate)); + if (ret != sizeof(ate)) + { + printf("%s:write ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* write gc_done ate */ + + fill_gc_done_ate(&ate); + ret = write(mtd_fd, &ate, sizeof(ate)); + if (ret != sizeof(ate)) + { + printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + close(mtd_fd); + mtd_fd = -1; + + /* now start up */ + + ret = setup(ctx); + if (ret < 0) + { + printf("%s:setup failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + nvs_fd = open("/dev/config", 0); + if (nvs_fd < 0) + { + printf("%s:open failed, ret=%d\n", __func__, nvs_fd); + goto test_fail; + } + + strlcpy(data.name, TEST_KEY1, sizeof(data.name)); + data.configdata = (FAR uint8_t *)rd_buf; + data.len = sizeof(rd_buf); + ret = ioctl(nvs_fd, CFGDIOC_GETCONFIG, &data); + if (ret < 0) + { + printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + if (strncmp(rd_buf, wr_buf_1, sizeof(rd_buf)) != 0) + { + printf("%s:failed, RD buff should be equal to the first WR buff " + "because subsequent write operation has failed\n", + __func__); + goto test_fail; + } + + close(nvs_fd); + nvs_fd = -1; + + /* at the end of test, erase all blocks */ + + ret = teardown(); + if (ret < 0) + { + printf("%s:teardown failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + printf("%s: success\n", __func__); + return; + +test_fail: + + printf("%s: failed\n", __func__); + if (nvs_fd > 0) + { + close(nvs_fd); + } + + if (mtd_fd > 0) + { + close(mtd_fd); + } + + return; +} + +/**************************************************************************** + * Name: test_nvs_gc + ****************************************************************************/ + +static void test_nvs_gc(struct mtdnvs_ctx_s *ctx) +{ + int ret; + int fd = -1; + uint8_t buf[44]; + uint8_t rd_buf[44]; + struct config_data_s data; + uint16_t i; + uint16_t id; + const uint16_t max_id = 10; + + /* 4096 * 2 / (44 + 4 + 16) = 128, 129 write will trigger GC. */ + + const uint16_t max_writes = 129; + + printf("%s: test begin\n", __func__); + + ret = setup(ctx); + if (ret < 0) + { + printf("%s:setup failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + fd = open("/dev/config", 0); + if (fd < 0) + { + printf("%s:open failed, ret=%d\n", __func__, fd); + goto test_fail; + } + + for (i = 0; i < max_writes; i++) + { + id = (i % max_id); + uint8_t id_data = id + max_id * (i / max_id); + + memset(buf, id_data, sizeof(buf)); + + /* 4 byte key */ + + sprintf(data.name, "k%02d", id); + data.configdata = buf; + data.len = sizeof(buf); + + ret = ioctl(fd, CFGDIOC_SETCONFIG, &data); + if (ret != 0) + { + printf("%s:CFGDIOC_SETCONFIG failed, ret=%d\n", __func__, ret); + ret = -EIO; + goto test_fail; + } + } + + for (id = 0; id < max_id; id++) + { + /* 4 byte key */ + + sprintf(data.name, "k%02d", id); + data.configdata = rd_buf; + data.len = sizeof(rd_buf); + + ret = ioctl(fd, CFGDIOC_GETCONFIG, &data); + if (ret != 0) + { + printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret); + ret = -EIO; + goto test_fail; + } + + for (i = 0; i < sizeof(rd_buf); i++) + { + rd_buf[i] = rd_buf[i] % max_id; + buf[i] = id; + } + + if (memcmp(buf, rd_buf, sizeof(rd_buf)) != 0) + { + printf("RD buff should be equal to the WR buff\n"); + goto test_fail; + } + } + + close(fd); + fd = -1; + + ret = mtdconfig_unregister(); + if (ret < 0) + { + printf("%s:mtdconfig_unregister failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = setup(ctx); + if (ret < 0) + { + printf("%s:setup failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + fd = open("/dev/config", 0); + if (fd < 0) + { + printf("%s:open failed, ret=%d\n", __func__, fd); + goto test_fail; + } + + for (id = 0; id < max_id; id++) + { + /* 4 byte key */ + + sprintf(data.name, "k%02d", id); + data.configdata = rd_buf; + data.len = sizeof(rd_buf); + + ret = ioctl(fd, CFGDIOC_GETCONFIG, &data); + if (ret != 0) + { + printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret); + ret = -EIO; + goto test_fail; + } + + for (i = 0; i < sizeof(rd_buf); i++) + { + rd_buf[i] = rd_buf[i] % max_id; + buf[i] = id; + } + + if (memcmp(buf, rd_buf, sizeof(rd_buf)) != 0) + { + printf("RD buff should be equal to the WR buff\n"); + goto test_fail; + } + } + + close(fd); + fd = -1; + + /* at the end of test, erase all blocks */ + + ret = teardown(); + if (ret < 0) + { + printf("%s:teardown failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + printf("%s: success\n", __func__); + + return; + +test_fail: + if (fd >= 0) + close(fd); + + printf("%s: failed\n", __func__); + + return; +} + +/**************************************************************************** + * Name: write_content + ****************************************************************************/ + +static int write_content(uint16_t max_id, uint16_t begin, uint16_t end) +{ + uint8_t buf[44]; + int fd = -1; + int ret; + struct config_data_s data; + uint16_t i; + + fd = open("/dev/config", 0); + if (fd < 0) + { + printf("%s:open failed, ret=%d\n", __func__, fd); + return -errno; + } + + for (i = begin; i < end; i++) + { + uint8_t id = (i % max_id); + uint8_t id_data = id + max_id * (i / max_id); + + memset(buf, id_data, sizeof(buf)); + + /* 4 byte key */ + + sprintf(data.name, "k%02d", id); + data.configdata = buf; + data.len = sizeof(buf); + + ret = ioctl(fd, CFGDIOC_SETCONFIG, &data); + if (ret != 0) + { + printf("%s:CFGDIOC_SETCONFIG failed, ret=%d\n", __func__, ret); + ret = -EIO; + goto test_fail; + } + } + + close(fd); + return ret; + +test_fail: + if (fd >= 0) + close(fd); + + return ret; +} + +/**************************************************************************** + * Name: check_content + ****************************************************************************/ + +static int check_content(uint16_t max_id) +{ + uint8_t rd_buf[44]; + uint8_t buf[44]; + int fd = -1; + int ret; + struct config_data_s data; + + fd = open("/dev/config", 0); + if (fd < 0) + { + printf("%s:open failed, ret=%d\n", __func__, fd); + return -errno; + } + + for (uint16_t id = 0; id < max_id; id++) + { + /* 4 byte key */ + + sprintf(data.name, "k%02d", id); + data.configdata = rd_buf; + data.len = sizeof(rd_buf); + + ret = ioctl(fd, CFGDIOC_GETCONFIG, &data); + if (ret != 0) + { + printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret); + ret = -EIO; + goto test_fail; + } + + for (uint16_t i = 0; i < sizeof(rd_buf); i++) + { + rd_buf[i] = rd_buf[i] % max_id; + buf[i] = id; + } + + if (memcmp(buf, rd_buf, sizeof(rd_buf)) != 0) + { + printf("RD buff should be equal to the WR buff\n"); + goto test_fail; + } + } + + close(fd); + return ret; + +test_fail: + + if (fd >= 0) + close(fd); + + return ret; +} + +/**************************************************************************** + * Name: test_nvs_gc_3sectors + * Description: Full round of GC over 3 sectors + ****************************************************************************/ + +static void test_nvs_gc_3sectors(struct mtdnvs_ctx_s *ctx) +{ + int ret; + const uint16_t max_id = 64; + + /* 4096 * 2 / (44 + 4 + 16) = 128, 129 write will trigger GC. */ + + const uint16_t max_writes = 129; + + /* 4096 / (44 + 4 + 16) = 64, 129 + 64 write will trigger 2st GC. */ + + const uint16_t max_writes_2 = 129 + 64; + const uint16_t max_writes_3 = 129 + 64 + 64; + const uint16_t max_writes_4 = 129 + 64 + 64 + 64; + + printf("%s: test begin\n", __func__); + + ret = setup(ctx); + if (ret < 0) + { + printf("%s:setup failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* Trigger 1st GC */ + + ret = write_content(max_id, 0, max_writes); + if (ret < 0) + { + printf("%s:1st GC write failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = check_content(max_id); + if (ret < 0) + { + printf("%s:1st GC check failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = mtdconfig_unregister(); + if (ret < 0) + { + printf("%s:1st mtdconfig_unregister failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = setup(ctx); + if (ret < 0) + { + printf("%s:1st setup failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = check_content(max_id); + if (ret < 0) + { + printf("%s:1st GC check failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* Trigger 2nd GC */ + + ret = write_content(max_id, max_writes, max_writes_2); + if (ret < 0) + { + printf("%s:2st GC write failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = check_content(max_id); + if (ret < 0) + { + printf("%s:2st GC check failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = mtdconfig_unregister(); + if (ret < 0) + { + printf("%s:2st mtdconfig_unregister failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = setup(ctx); + if (ret < 0) + { + printf("%s:2st setup failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = check_content(max_id); + if (ret < 0) + { + printf("%s:2st GC check failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* Trigger 3rd GC */ + + ret = write_content(max_id, max_writes_2, max_writes_3); + if (ret < 0) + { + printf("%s:3st GC write failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = check_content(max_id); + if (ret < 0) + { + printf("%s:3st GC check failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = mtdconfig_unregister(); + if (ret < 0) + { + printf("%s:3st mtdconfig_unregister failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = setup(ctx); + if (ret < 0) + { + printf("%s:3st setup failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = check_content(max_id); + if (ret < 0) + { + printf("%s:3st GC check failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* Trigger 4th GC */ + + ret = write_content(max_id, max_writes_3, max_writes_4); + if (ret < 0) + { + printf("%s:4st GC write failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = check_content(max_id); + if (ret < 0) + { + printf("%s:4st GC check failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = mtdconfig_unregister(); + if (ret < 0) + { + printf("%s:4st mtdconfig_unregister failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = setup(ctx); + if (ret < 0) + { + printf("%s:4st setup failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = check_content(max_id); + if (ret < 0) + { + printf("%s:4st GC check failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* at the end of test, erase all blocks */ + + ret = teardown(); + if (ret < 0) + { + printf("%s:teardown failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + printf("%s: success\n", __func__); + + return; + +test_fail: + + printf("%s: failed\n", __func__); + + return; +} + +/**************************************************************************** + * Name: test_nvs_corrupted_sector_close + ****************************************************************************/ + +static void test_nvs_corrupted_sector_close(struct mtdnvs_ctx_s *ctx) +{ + int ret; + uint8_t rd_buf[512]; + uint8_t wr_buf[512]; + char key1[] = TEST_KEY1; + int mtd_fd = -1; + int nvs_fd = -1; + int loop_section; + int i; + uint8_t erase_value = CONFIG_MTD_CONFIG_ERASEDVALUE; + struct nvs_ate ate; + struct config_data_s data; + + printf("%s: test begin\n", __func__); + + mtd_fd = open(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, O_RDWR); + if (mtd_fd < 0) + { + printf("%s:mtdnvs_register failed, ret=%d\n", __func__, mtd_fd); + goto test_fail; + } + + /* lets simulate loss of close at the beginning of gc */ + + for (loop_section = 0; + loop_section < + CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_COUNT - 1; + loop_section++) + { + /* write valid data */ + + for (i = 0; i < 2 ; i++) + { + ret = write(mtd_fd, key1, sizeof(key1)); + if (ret != sizeof(key1)) + { + printf("%s:write key1 failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + memset(wr_buf, 'A' + i, sizeof(wr_buf)); + wr_buf[sizeof(wr_buf) - 1] = '\0'; + + ret = write(mtd_fd, wr_buf, sizeof(wr_buf)); + if (ret != sizeof(wr_buf)) + { + printf("%s:write data1 failed, ret=%d\n", __func__, ret); + goto test_fail; + } + } + + /* set unused flash to 0xff */ + + for (i = 2 * (sizeof(key1) + sizeof(wr_buf)); + i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 4 * 16; + i++) + { + ret = write(mtd_fd, &erase_value, sizeof(erase_value)); + if (ret != sizeof(erase_value)) + { + printf("%s:erase failed, ret=%d\n", __func__, ret); + goto test_fail; + } + } + + /* write ate 2 times */ + + fill_ate(&ate, key1, sizeof(wr_buf), sizeof(wr_buf) + sizeof(key1), + (loop_section == + CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_COUNT - 2) + ? false : true); + ret = write(mtd_fd, &ate, sizeof(ate)); + if (ret != sizeof(ate)) + { + printf("%s:write ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + fill_ate(&ate, key1, sizeof(wr_buf), 0, true); + ret = write(mtd_fd, &ate, sizeof(ate)); + if (ret != sizeof(ate)) + { + printf("%s:write ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* write gc_done ate */ + + fill_gc_done_ate(&ate); + ret = write(mtd_fd, &ate, sizeof(ate)); + if (ret != sizeof(ate)) + { + printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + if (loop_section == + CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_COUNT - 2) + { + fill_corrupted_close_ate(&ate); + } + else + { + fill_close_ate(&ate, + CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 4 * 16); + } + + ret = write(mtd_fd, &ate, sizeof(ate)); + if (ret != sizeof(ate)) + { + printf("%s:write close ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + } + + close(mtd_fd); + mtd_fd = -1; + + ret = setup(ctx); + if (ret < 0) + { + printf("%s:setup failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + nvs_fd = open("/dev/config", 0); + if (nvs_fd < 0) + { + printf("%s:open failed, ret=%d\n", __func__, nvs_fd); + goto test_fail; + } + + strlcpy(data.name, TEST_KEY1, sizeof(data.name)); + data.configdata = rd_buf; + data.len = sizeof(rd_buf); + + ret = ioctl(nvs_fd, CFGDIOC_GETCONFIG, &data); + if (ret != 0) + { + printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret); + ret = -EIO; + goto test_fail; + } + + if (memcmp(rd_buf, wr_buf, sizeof(rd_buf)) != 0) + { + printf("%s:strncmp failed\n", __func__); + ret = -EACCES; + goto test_fail; + } + + /* Ensure that the NVS is able to store new content. */ + + if (execute_long_pattern_write(TEST_KEY2) != 0) + { + printf("%s:write again failed\n", __func__); + ret = -EACCES; + goto test_fail; + } + + close(nvs_fd); + nvs_fd = -1; + + /* at the end of test, erase all blocks */ + + ret = teardown(); + if (ret < 0) + { + printf("%s:teardown failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + printf("%s: success\n", __func__); + + return; + +test_fail: + + if (nvs_fd >= 0) + close(nvs_fd); + + if (mtd_fd >= 0) + close(mtd_fd); + + printf("%s: failed\n", __func__); + + return; +} + +/**************************************************************************** + * Name: test_nvs_full_sector + * Description: Test case when storage become full, + * so only deletion is possible. + ****************************************************************************/ + +static void test_nvs_full_sector(struct mtdnvs_ctx_s *ctx) +{ + int ret; + uint16_t filling_id = 0; + uint16_t i; + uint16_t data_read; + int fd = -1; + struct config_data_s data; + + printf("%s: test begin\n", __func__); + + ret = setup(ctx); + if (ret < 0) + { + printf("%s:setup failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + fd = open("/dev/config", 0); + if (fd < 0) + { + printf("%s:open failed, ret=%d\n", __func__, fd); + goto test_fail; + } + + while (1) + { + sprintf(data.name, "k%04x", filling_id); + data.configdata = (FAR uint8_t *)&filling_id; + data.len = sizeof(filling_id); + + ret = ioctl(fd, CFGDIOC_SETCONFIG, &data); + if (ret == -1 && errno == ENOSPC) + { + break; + } + else if (ret != 0) + { + printf("%s:CFGDIOC_SETCONFIG failed, ret=%d\n", __func__, ret); + ret = -EIO; + goto test_fail; + } + + filling_id++; + } + + /* check whether can delete whatever from full storage */ + + sprintf(data.name, "k%04x", 1); + data.configdata = NULL; + data.len = 0; + + ret = ioctl(fd, CFGDIOC_DELCONFIG, &data); + if (ret != 0) + { + printf("%s:CFGDIOC_DELCONFIG failed, ret=%d\n", __func__, ret); + ret = -EIO; + goto test_fail; + } + + /* the last sector is full now, test re-initialization */ + + close(fd); + fd = -1; + mtdconfig_unregister(); + + ret = setup(ctx); + if (ret < 0) + { + printf("%s:setup failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + fd = open("/dev/config", 0); + if (fd < 0) + { + printf("%s:open failed, ret=%d\n", __func__, fd); + goto test_fail; + } + + sprintf(data.name, "k%04x", filling_id); + data.configdata = (FAR uint8_t *)&filling_id; + data.len = sizeof(filling_id); + + ret = ioctl(fd, CFGDIOC_SETCONFIG, &data); + if (ret != 0) + { + printf("%s:CFGDIOC_SETCONFIG failed, ret=%d\n", __func__, ret); + ret = -EIO; + goto test_fail; + } + + /* sanitycheck on NVS content */ + + for (i = 0; i <= filling_id; i++) + { + sprintf(data.name, "k%04x", i); + data.configdata = (FAR uint8_t *)&data_read; + data.len = sizeof(data_read); + + ret = ioctl(fd, CFGDIOC_GETCONFIG, &data); + + if (i == 1) + { + if (ret != -1 || errno != ENOENT) + { + printf("%s:shouldn't found the entry: %d\n", __func__, i); + ret = -EIO; + goto test_fail; + } + } + else if (ret != 0) + { + printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret); + ret = -EIO; + goto test_fail; + } + else + { + if (data_read != i) + { + printf("%s:read data %d \n", __func__, data_read); + printf("%s:read expected %d\n", __func__, i); + printf("%s:read unexpected data: %d instead of %d\n", + __func__, data_read, i); + ret = -EIO; + goto test_fail; + } + } + } + + close(fd); + + /* at the end of test, erase all blocks */ + + ret = teardown(); + if (ret < 0) + { + printf("%s:teardown failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + printf("%s: success\n", __func__); + + return; + +test_fail: + + if (fd >= 0) + close(fd); + + printf("%s: failed\n", __func__); + + return; +} + +/**************************************************************************** + * Name: test_nvs_gc_corrupt_close_ate + * Description: Test that garbage-collection can + * recover all ate's even when the last ate, ie close_ate, + * is corrupt. In this test the close_ate is set to point to the + * last ate at -5. A valid ate is however present at -6. + * Since the close_ate has an invalid crc8, the offset + * should not be used and a recover of the + * last ate should be done instead. + ****************************************************************************/ + +static void test_nvs_gc_corrupt_close_ate(struct mtdnvs_ctx_s *ctx) +{ + struct nvs_ate ate; + struct nvs_ate close_ate; + int mtd_fd = -1; + int nvs_fd = -1; + int ret; + int i; + uint8_t erase_value = CONFIG_MTD_CONFIG_ERASEDVALUE; + struct config_data_s data; + char rd_buf[50]; + + printf("%s: test begin\n", __func__); + + mtd_fd = open(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, O_RDWR); + if (mtd_fd < 0) + { + printf("%s:mtdnvs_register failed, ret=%d\n", __func__, mtd_fd); + goto test_fail; + } + + /* write valid data */ + + ret = write(mtd_fd, TEST_KEY1, strlen(TEST_KEY1) + 1); + if (ret != strlen(TEST_KEY1) + 1) + { + printf("%s:write key1 failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + ret = write(mtd_fd, TEST_DATA1, strlen(TEST_DATA1) + 1); + if (ret != strlen(TEST_DATA1) + 1) + { + printf("%s:write data1 failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* set unused flash to 0xff */ + + for (i = strlen(TEST_KEY1) + strlen(TEST_DATA1) + 2; + i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 6 * 16; + i++) + { + ret = write(mtd_fd, &erase_value, sizeof(erase_value)); + if (ret != sizeof(erase_value)) + { + printf("%s:erase failed, ret=%d\n", __func__, ret); + goto test_fail; + } + } + + /* Write valid ate at -6 */ + + fill_ate(&ate, TEST_KEY1, strlen(TEST_DATA1) + 1, 0, false); + ret = write(mtd_fd, &ate, sizeof(ate)); + if (ret != sizeof(ate)) + { + printf("%s:write ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* set unused flash to 0xff */ + + for (i = CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 5 * 16; + i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 2 * 16; + i++) + { + ret = write(mtd_fd, &erase_value, sizeof(erase_value)); + if (ret != sizeof(erase_value)) + { + printf("%s:erase failed, ret=%d\n", __func__, ret); + goto test_fail; + } + } + + /* write gc_done ate */ + + fill_gc_done_ate(&ate); + ret = write(mtd_fd, &ate, sizeof(ate)); + if (ret != sizeof(ate)) + { + printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* write invalid close ate, mark section 0 as closed */ + + fill_corrupted_close_ate(&close_ate); + ret = write(mtd_fd, &close_ate, sizeof(close_ate)); + if (ret != sizeof(ate)) + { + printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* set unused flash to 0xff in section 1 */ + + for (i = CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE; + i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 16; + i++) + { + ret = write(mtd_fd, &erase_value, sizeof(erase_value)); + if (ret != sizeof(erase_value)) + { + printf("%s:erase failed, ret=%d\n", __func__, ret); + goto test_fail; + } + } + + /* write invalid close ate, mark section 1 as closed */ + + fill_corrupted_close_ate(&close_ate); + ret = write(mtd_fd, &close_ate, sizeof(close_ate)); + if (ret != sizeof(ate)) + { + printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + close(mtd_fd); + mtd_fd = -1; + + ret = setup(ctx); + if (ret < 0) + { + printf("%s:setup failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + nvs_fd = open("/dev/config", 0); + if (nvs_fd < 0) + { + printf("%s:open failed, ret=%d\n", __func__, nvs_fd); + goto test_fail; + } + + strlcpy(data.name, TEST_KEY1, sizeof(data.name)); + data.configdata = (FAR uint8_t *)rd_buf; + data.len = sizeof(rd_buf); + + ret = ioctl(nvs_fd, CFGDIOC_GETCONFIG, &data); + if (ret != 0) + { + printf("%s:NVSIOC_READ unexpected failure: %d\n", __func__, ret); + goto test_fail; + } + + if (strncmp(rd_buf, TEST_DATA1, sizeof(rd_buf)) != 0) + { + printf("%s:unexpected value\n", __func__); + goto test_fail; + } + + close(nvs_fd); + nvs_fd = -1; + + /* at the end of test, erase all blocks */ + + ret = teardown(); + if (ret < 0) + { + printf("%s:teardown failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + printf("%s: success\n", __func__); + + return; + +test_fail: + + if (nvs_fd >= 0) + close(nvs_fd); + + if (mtd_fd >= 0) + close(mtd_fd); + + printf("%s: failed\n", __func__); + + return; +} + +/**************************************************************************** + * Name: test_nvs_gc_corrupt_ate + * Description: Test that garbage-collection correctly handles corrupt ate's. + ****************************************************************************/ + +static void test_nvs_gc_corrupt_ate(struct mtdnvs_ctx_s *ctx) +{ + struct nvs_ate ate; + struct nvs_ate close_ate; + int mtd_fd = -1; + int ret; + int i; + uint8_t erase_value = CONFIG_MTD_CONFIG_ERASEDVALUE; + + printf("%s: test begin\n", __func__); + + fill_corrupted_ate(&ate, TEST_KEY1, 10, 0); + + mtd_fd = open(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, O_RDWR); + if (mtd_fd < 0) + { + printf("%s:mtdnvs_register failed, ret=%d\n", __func__, mtd_fd); + goto test_fail; + } + + /* set unused flash to 0xff */ + + for (i = 0 ; + i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE / 2; i++) + { + ret = write(mtd_fd, &erase_value, sizeof(erase_value)); + if (ret != sizeof(erase_value)) + { + printf("%s:erase failed, ret=%d\n", __func__, ret); + goto test_fail; + } + } + + /* Write invalid ate */ + + ret = write(mtd_fd, &ate, sizeof(ate)); + if (ret != sizeof(ate)) + { + printf("%s:write ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + for (i = CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE / 2 + 16; + i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 32; i++) + { + ret = write(mtd_fd, &erase_value, sizeof(erase_value)); + if (ret != sizeof(erase_value)) + { + printf("%s:erase failed, ret=%d\n", __func__, ret); + goto test_fail; + } + } + + /* write gc_done ate */ + + fill_gc_done_ate(&ate); + ret = write(mtd_fd, &ate, sizeof(ate)); + if (ret != sizeof(ate)) + { + printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* write close ate, mark section 0 as closed */ + + fill_close_ate(&close_ate, + CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE / 2); + ret = write(mtd_fd, &close_ate, sizeof(close_ate)); + if (ret != sizeof(ate)) + { + printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* set unused flash to 0xff in section 1 */ + + for (i = CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE; + i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 16; + i++) + { + ret = write(mtd_fd, &erase_value, sizeof(erase_value)); + if (ret != sizeof(erase_value)) + { + printf("%s:erase failed, ret=%d\n", __func__, ret); + goto test_fail; + } + } + + /* write close ate, mark section 1 as closed */ + + ret = write(mtd_fd, &close_ate, sizeof(close_ate)); + if (ret != sizeof(ate)) + { + printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + close(mtd_fd); + mtd_fd = -1; + + ret = setup(ctx); + if (ret < 0) + { + printf("%s:setup failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + /* at the end of test, erase all blocks */ + + ret = teardown(); + if (ret < 0) + { + printf("%s:teardown failed, ret=%d\n", __func__, ret); + goto test_fail; + } + + printf("%s: success\n", __func__); + + return; + +test_fail: + + if (mtd_fd >= 0) + close(mtd_fd); + + printf("%s: failed\n", __func__); + + return; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: fstest_main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + FAR struct mtdnvs_ctx_s *ctx; + int option; + + ctx = malloc(sizeof(struct mtdnvs_ctx_s)); + if (ctx == NULL) + { + printf("malloc ctx feild,exit!\n"); + exit(1); + } + + memset(ctx, 0, sizeof(struct mtdnvs_ctx_s)); + + strlcpy(ctx->mountdir, CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, + sizeof(ctx->mountdir)); + + /* Opt Parse */ + + while ((option = getopt(argc, argv, ":m:hn:")) != -1) + { + switch (option) + { + case 'm': + strcpy(ctx->mountdir, optarg); + break; + case 'h': + show_useage(); + free(ctx); + exit(0); + case ':': + printf("Error: Missing required argument\n"); + free(ctx); + exit(1); + case '?': + printf("Error: Unrecognized option\n"); + free(ctx); + exit(1); + } + } + + /* Set up memory monitoring */ + + ctx->mmbefore = mallinfo(); + ctx->mmprevious = ctx->mmbefore; + + test_nvs_mount(ctx); + test_nvs_write(ctx); + test_nvs_corrupt_expire(ctx); + test_nvs_corrupted_write(ctx); + test_nvs_gc(ctx); + test_nvs_gc_3sectors(ctx); + test_nvs_corrupted_sector_close(ctx); + test_nvs_full_sector(ctx); + test_nvs_gc_corrupt_close_ate(ctx); + test_nvs_gc_corrupt_ate(ctx); + + /* Show memory usage */ + + mtdnvs_endmemusage(ctx); + fflush(stdout); + free(ctx); + return 0; +} +