You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/08/31 12:33:25 UTC
[incubator-nuttx-apps] branch master updated: testing:add MTD Fail-safe config-data tests
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: 田昕 <ti...@xiaomi.com>
AuthorDate: Mon Aug 22 10:20:06 2022 +0800
testing:add MTD Fail-safe config-data tests
Signed-off-by: 田昕 <ti...@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;
+}
+