nuttx-apps/testing/mtd_config_fs/mtd_config_fs_test_main.c

2527 lines
61 KiB
C
Raw Normal View History

/****************************************************************************
* 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;
uint8_t erasestate;
};
/****************************************************************************
* 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 mtdnvs_ctx_s *ctx,
FAR struct nvs_ate *close_ate)
{
memset((FAR void *)close_ate, ctx->erasestate,
sizeof(struct nvs_ate));
close_ate->id = 0xffffffff;
close_ate->len = 0U;
}
/****************************************************************************
* Name: fill_close_ate
****************************************************************************/
static void fill_close_ate(FAR struct mtdnvs_ctx_s *ctx,
FAR struct nvs_ate *close_ate, int offset)
{
memset((FAR void *)close_ate, ctx->erasestate,
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 mtdnvs_ctx_s *ctx,
FAR struct nvs_ate *gc_done_ate)
{
memset((FAR void *)gc_done_ate, ctx->erasestate,
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 mtdnvs_ctx_s *ctx, FAR struct nvs_ate *ate,
FAR const char *key, uint16_t len, uint16_t offset,
bool expired)
{
memset((FAR void *)ate, ctx->erasestate,
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 mtdnvs_ctx_s *ctx,
FAR struct nvs_ate *ate, FAR const char *key,
uint16_t len, uint16_t offset)
{
memset((FAR void *)ate, ctx->erasestate,
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 = MTD_IOCTL(sys_node->u.i_mtd, MTDIOC_ERASESTATE,
(unsigned long)((uintptr_t)&ctx->erasestate));
if (ret < 0)
{
printf("ERROR: MTD ioctl(MTDIOC_ERASESTATE) failed: %d\n", ret);
return ret;
}
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__);
}
/****************************************************************************
* 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__);
}
/****************************************************************************
* 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 = ctx->erasestate;
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(ctx, &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(ctx, &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(ctx, &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(ctx, &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__);
}
/****************************************************************************
* 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 = ctx->erasestate;
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(ctx, &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(ctx, &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);
}
}
/****************************************************************************
* 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__);
}
/****************************************************************************
* 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__);
}
/****************************************************************************
* 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 = ctx->erasestate;
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(ctx, &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(ctx, &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(ctx, &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(ctx, &ate);
}
else
{
fill_close_ate(ctx, &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__);
}
/****************************************************************************
* 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__);
}
/****************************************************************************
* 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 = ctx->erasestate;
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(ctx, &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(ctx, &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(ctx, &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(ctx, &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__);
}
/****************************************************************************
* 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 = ctx->erasestate;
printf("%s: test begin\n", __func__);
fill_corrupted_ate(ctx, &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(ctx, &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(ctx, &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__);
}
/****************************************************************************
* Name: test_nvs_gc_touched_deleted_ate
* Description: Test case when writing and gc touched A prev entry
* which was deleted.
****************************************************************************/
static void test_nvs_gc_touched_deleted_ate(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);
/* ENOSPC will be accompanied by gc 3 times(if 3 blocks)
* block will change three times:
* block: 0, 1, 2
* A B gc
* (gc==1) gc B A
* (gc==2) B gc A
* (gc==3) B A gc
*/
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++;
}
/* Now delete last record.
* The layout should be:
* block: 0, 1, 2
* B(deleted) A gc
*/
sprintf(data.name, "k%04x", filling_id - 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;
}
/* Now input B again,
* we should trigger gc for once, and we needn't search for
* the old one again because it is expired.
*/
filling_id -= 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 != 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 (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__);
}
/****************************************************************************
* Name: test_nvs_gc_touched_expired_ate
* Description: Test case when writing and gc touched A prev entry
* which was moved by gc.
****************************************************************************/
static void test_nvs_gc_touched_expired_ate(struct mtdnvs_ctx_s *ctx)
{
int ret;
uint16_t filling_id = 0;
uint16_t update_id;
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++;
}
/* Now delete,
* the layout should be:
* block: 0, 1, 2
* B A(deleted) gc
*/
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;
}
/* Now update A.
* It should trigger gc for twice, and we need to search for
* the old one again as gc has touched the old one. We will
* find it and expire the old A.
*/
update_id = 3;
sprintf(data.name, "k%04x", 2);
data.configdata = (FAR uint8_t *)&update_id;
data.len = sizeof(update_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 - 1; 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 (i == 2)
{
if (data_read != 3)
{
printf("%s:read data %d \n", __func__, data_read);
printf("%s:read expected %d\n", __func__, 3);
printf("%s:read unexpected data: %d instead of %d\n",
__func__, data_read, 3);
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__);
}
/****************************************************************************
* Name: test_nvs_gc_not_touched_expired_ate
* Description: Test case when writing and gc not touched a prev entry
* which was moved by gc.
****************************************************************************/
static void test_nvs_gc_not_touched_expired_ate(struct mtdnvs_ctx_s *ctx)
{
int ret;
uint16_t filling_id = 0;
uint16_t update_id;
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++;
}
/* Now delete last record,
* the layout should be:
* block: 0, 1, 2
* B(deleted) A gc
*/
sprintf(data.name, "k%04x", filling_id - 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;
}
/* Now udpate A again.
* We should trigger gc for once, and we won't need to search for
* the old one again after gc.
*/
update_id = 3;
sprintf(data.name, "k%04x", 2);
data.configdata = (FAR uint8_t *)&update_id;
data.len = sizeof(update_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 - 1; 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 == filling_id - 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 (i == 2)
{
if (data_read != 3)
{
printf("%s:read data %d \n", __func__, data_read);
printf("%s:read expected %d\n", __func__, 3);
printf("%s:read unexpected data: %d instead of %d\n",
__func__, data_read, 3);
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__);
}
/****************************************************************************
* 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':
strlcpy(ctx->mountdir, optarg,
sizeof(ctx->mountdir));
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);
test_nvs_gc_touched_deleted_ate(ctx);
test_nvs_gc_touched_expired_ate(ctx);
test_nvs_gc_not_touched_expired_ate(ctx);
/* Show memory usage */
mtdnvs_endmemusage(ctx);
fflush(stdout);
free(ctx);
return 0;
}