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;
+}
+