Compare commits

...

45 Commits

Author SHA1 Message Date
S.J.R. van Schaik adbd045af5 mufs: fix offset calculation in mufs_seek() 7 years ago
S.J.R. van Schaik 23b478e582 shell: fix off by one in draw_progress() 7 years ago
S.J.R. van Schaik 635df8e06f shell: mufs: use fprintf in shell_stat() 7 years ago
S.J.R. van Schaik 9a2888341d shell: mufs: fix order 7 years ago
S.J.R. van Schaik 5cc4824fdb shell: mufs: implement mufs cp to copy files 7 years ago
S.J.R. van Schaik 1638f9b0f1 shell: implement draw_progress() as a convenience function 7 years ago
S.J.R. van Schaik 4c2f4eab24 ftl: call trace_path() in ftl_sync() to get the page descriptor to write out 7 years ago
S.J.R. van Schaik dddba0af8f ftl: check outstanding flags in ftl_seek() 7 years ago
S.J.R. van Schaik 52a7ec1fae mufs: call flash_sync() after each flash_write() 7 years ago
S.J.R. van Schaik bf1f0f4bee test: ftl: add tests for ftl_read() 7 years ago
S.J.R. van Schaik 0d1ffa71fb ftl: change order of decrementing the used page count to be more logical 7 years ago
S.J.R. van Schaik bea048006a ftl: separate code to start a transaction 7 years ago
S.J.R. van Schaik 65d269e2d4 ftl: maintain state for the transaction that is currently active 7 years ago
S.J.R. van Schaik e40bd819d6 test: flash: mock flash_write0() and flash_copy() 7 years ago
S.J.R. van Schaik fc1476d9dd test: ftl: add test to check if an exact match works for trace_path() 7 years ago
S.J.R. van Schaik 851c0cb7a6 test: ftl: check returned page descriptor for trace_path() 7 years ago
S.J.R. van Schaik ae3b7933b7 test: ftl: add tests for trace_path() 7 years ago
S.J.R. van Schaik f3eb5df644 test: ftl: add tests for ftl_is_mapped() 7 years ago
S.J.R. van Schaik 5004fed4aa test: reorganise code 7 years ago
S.J.R. van Schaik 6243dabea8 test: ftl: add tests for find_head() 7 years ago
S.J.R. van Schaik 920a59499b test: ftl: add tests for find_root() 7 years ago
S.J.R. van Schaik b8426f12f2 test: ftl: mock read_page_desc() 7 years ago
S.J.R. van Schaik 1153f2bdcd test: ftl: improve descriptions for the test cases 7 years ago
S.J.R. van Schaik 6dcfd0d322 test: add tests for find_last_group() 7 years ago
S.J.R. van Schaik d778e5a325 test: add tests for find_last_block() 7 years ago
S.J.R. van Schaik c6744d6975 test: ftl: add tests for find_block() 7 years ago
S.J.R. van Schaik 73057f6f25 test: only call memcpy() in __wrap_flash_read() and __wrap_flash_write() when there is something to copy 7 years ago
S.J.R. van Schaik 0da262cc0f test: implement __wrap_is_group_erased() and __wrap_find_block() 7 years ago
S.J.R. van Schaik bbfb9ca800 ftl: mock is_group_erased() and find_block() and simplify implementation of binary search 7 years ago
S.J.R. van Schaik 8e4dc856e7 test: ftl: separate mocked functions 7 years ago
S.J.R. van Schaik 2ff1003430 test: ftl: add tests for write_upage() 7 years ago
S.J.R. van Schaik c1a7b885f2 test: call __real_write_page_desc() in tests and mock prepare_head() and write_page_desc() 7 years ago
S.J.R. van Schaik d2aa954573 test: remove redundant declarations 7 years ago
S.J.R. van Schaik f951d695e8 test: flash: use different mocking technique 7 years ago
S.J.R. van Schaik b4ecfb527c ftl: check magic in read_page_group() 7 years ago
S.J.R. van Schaik ed49402e3b test: ftl: add tests for read_page_group() 7 years ago
S.J.R. van Schaik 3e0df6537a test: ftl: implement tests for write_page_desc() 7 years ago
S.J.R. van Schaik 108178d1be test: flash: implement wrappers for flash_write() and flash_erase() 7 years ago
S.J.R. van Schaik a63e7656fe test: ftl: add tests for next_upage() 7 years ago
S.J.R. van Schaik e84226657c test: reorganise code 7 years ago
S.J.R. van Schaik 18d2adcb85 make: less strict warnings 7 years ago
S.J.R. van Schaik f0a8439c51 ftl: fix address calculation in read_page_desc() 7 years ago
S.J.R. van Schaik bbc799f805 test: add tests for find_block_div() and read_page_desc() 7 years ago
S.J.R. van Schaik b29e0b945d tbm: fix minor issues during compilation 7 years ago
S.J.R. van Schaik 82d765bee3 make: set up test framework 7 years ago
  1. 30
      Makefile
  2. 4
      include/console.h
  3. 15
      include/ftl.h
  4. 2
      include/mufs.h
  5. 3
      include/shell/progress.h
  6. 4
      scripts/Makefile.host
  7. 6
      source/Makefile
  8. 1
      source/core/Makefile
  9. 54
      source/core/flash.c
  10. 2
      source/drivers/sandbox_flash.c
  11. 77
      source/ftl/ftl.c
  12. 5
      source/ftl/gc.c
  13. 123
      source/ftl/map.c
  14. 4
      source/mufs/file.c
  15. 13
      source/mufs/tree.c
  16. 6
      source/platform/Makefile
  17. 3
      source/platform/stm32f0/Makefile
  18. 3
      source/platform/stm32f1/Makefile
  19. 1
      source/shell/Makefile
  20. 78
      source/shell/mufs.c
  21. 26
      source/shell/progress.c
  22. 34
      source/tests/Makefile
  23. 89
      source/tests/flash/mock.c
  24. 170
      source/tests/ftl/find_block.c
  25. 70
      source/tests/ftl/find_block_div.c
  26. 104
      source/tests/ftl/find_head.c
  27. 291
      source/tests/ftl/find_last_block.c
  28. 156
      source/tests/ftl/find_last_group.c
  29. 153
      source/tests/ftl/find_root.c
  30. 62
      source/tests/ftl/ftl_is_mapped.c
  31. 80
      source/tests/ftl/ftl_read.c
  32. 51
      source/tests/ftl/main.c
  33. 87
      source/tests/ftl/mock.c
  34. 3
      source/tests/ftl/mock.h
  35. 124
      source/tests/ftl/next_upage.c
  36. 182
      source/tests/ftl/read_page_desc.c
  37. 95
      source/tests/ftl/read_page_group.c
  38. 197
      source/tests/ftl/trace_path.c
  39. 258
      source/tests/ftl/write_page_desc.c
  40. 131
      source/tests/ftl/write_upage.c
  41. 23
      source/tests/main.c

@ -14,22 +14,25 @@ all: $(BUILD)/tbm
CFLAGS += -DTBM_VERSION=\"2017-07-27-dev\"
CFLAGS += -Iinclude
CFLAGS += -Wall -Wundef -Wextra -Wshadow -Wimplicit-function-declaration
CFLAGS += -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes -pedantic
CFLAGS += -Wall -Wextra -Wshadow -Wimplicit-function-declaration
CFLAGS += -Wredundant-decls -pedantic
CFLAGS += -D_XOPEN_SOURCE
CFLAGS += -std=c99
CFLAGS += -Os -flto
LDFLAGS += -Os -flto
TEST_LIBS += -lcmocka
-include source/Makefile
obj-y += source/bitops.o
obj-y += source/main.o
obj = $(addprefix $(BUILD)/, $(obj-y))
obj-y += source/core/flash.o
tbm-obj = $(addprefix $(BUILD)/, $(tbm-obj-y))
tbm-obj += $(obj)
obj = $(addprefix $(BUILD)/, $(obj-y))
test-obj = $(addprefix $(BUILD)/, $(test-obj-y))
test-obj += $(obj)
# Include the dependencies.
-include $(obj:.o=.d)
@ -52,9 +55,18 @@ $(BUILD)/%.o: %.c
@mkdir -p $(dir $@)
@$(CC) -c $< -o $@ $(CFLAGS) -MT $@ -MMD -MP -MF $(@:.o=.d)
$(BUILD)/tbm: $(obj) $(LDSCRIPT)
$(BUILD)/tbm: $(tbm-obj) $(LDSCRIPT)
@echo "LD $@"
@mkdir -p $(dir $@)
@$(LD) -o $@ $(CFLAGS) $(LDFLAGS) $(obj) $(LIBS)
@$(LD) -o $@ $(CFLAGS) $(LDFLAGS) $(tbm-obj) $(LIBS)
$(BUILD)/test: CFLAGS += $(TEST_CFLAGS)
$(BUILD)/test: $(test-obj)
@echo "LD $@"
@mkdir -p $(dir $@)
@$(LD) -o $@ $(CFLAGS) $(LDFLAGS) $(TEST_LDFLAGS) $(test-obj) $(LIBS) $(TEST_LIBS)
test: $(BUILD)/test
@$(BUILD)/test
.PHONY: clean
.PHONY: clean test

@ -2,6 +2,10 @@
#include <stdio.h>
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
struct console {
FILE *fp;
};

@ -15,11 +15,14 @@ struct ftl_page_desc {
uint32_t subtrees[32];
} __attribute__((packed));
struct ftl_tlb_entry {
uint32_t va, page;
uint8_t flags;
};
struct ftl_map {
struct flash_dev *dev;
unsigned flags;
uint32_t last_va;
uint32_t last_pa;
struct ftl_tlb_entry outstanding;
uint32_t offset;
uint32_t head, tail;
uint32_t root;
@ -33,9 +36,9 @@ struct ftl_map {
uint8_t epoch;
};
#define FTL_CACHED_VA BIT(0)
#define FTL_UNMAPPED BIT(1)
#define FTL_DIRTY BIT(2)
#define FTL_PRESENT BIT(7)
#define FTL_NO_MAPPING BIT(6)
#define FTL_AGE(x) (x & BITS(0, 3))
#define FTL_MAX_ATTEMPTS 8

@ -1,5 +1,7 @@
#pragma once
#include <stdint.h>
struct flash_dev;
struct mufs;
struct mufs_dir;

@ -0,0 +1,3 @@
#pragma once
void draw_progress(FILE *fp, unsigned long pos, unsigned max);

@ -1,7 +1,5 @@
CFLAGS += -g
obj-y += source/drivers/sandbox_flash.o
obj-y += source/drivers/stdio_console.o
CFLAGS += -DHAVE_SYS_TYPES_H=1
tools: $(BUILD)/create-image
all: tools

@ -1,4 +1,10 @@
-include source/core/Makefile
-include source/ftl/Makefile
-include source/mufs/Makefile
-include source/platform/Makefile
-include source/shell/Makefile
-include source/tests/Makefile
obj-y += source/bitops.o
tbm-obj-y += source/main.o

@ -0,0 +1 @@
tbm-obj-y += source/core/flash.o

@ -5,9 +5,9 @@
#include <flash.h>
#include <macros.h>
static int default_flash_is_val(struct flash_dev *dev, uint32_t addr, char val)
static int default_flash_is_val(struct flash_dev *dev, uint32_t addr, uint8_t val)
{
char data[32];
uint8_t data[32];
size_t i, nbytes, len = 1 << dev->log2_block_size;
addr <<= dev->log2_block_size;
@ -88,6 +88,11 @@ size_t flash_get_capacity(struct flash_dev *dev)
return dev->ops->get_capacity(dev);
}
#ifdef flash_read
#undef flash_read
#define flash_read __real_flash_read
#endif
size_t flash_read(struct flash_dev *dev, uint32_t addr, void *data, size_t len)
{
if (!dev)
@ -96,6 +101,16 @@ size_t flash_read(struct flash_dev *dev, uint32_t addr, void *data, size_t len)
return dev->ops->read(dev, addr, data, len);
}
#ifdef flash_read
#undef flash_read
#define flash_read __wrap_flash_read
#endif
#ifdef flash_write
#undef flash_write
#define flash_write __real_flash_write
#endif
size_t flash_write(struct flash_dev *dev, uint32_t addr, const void *data,
size_t len)
{
@ -105,6 +120,16 @@ size_t flash_write(struct flash_dev *dev, uint32_t addr, const void *data,
return dev->ops->write(dev, addr, data, len);
}
#ifdef flash_write
#undef flash_write
#define flash_write __wrap_flash_write
#endif
#ifdef flash_write0
#undef flash_write0
#define flash_write0 __real_flash_write0
#endif
size_t flash_write0(struct flash_dev *dev, uint32_t addr, size_t len)
{
uint8_t data[32];
@ -126,6 +151,16 @@ size_t flash_write0(struct flash_dev *dev, uint32_t addr, size_t len)
return ret;
}
#ifdef flash_write0
#undef flash_write0
#define flash_write0 __wrap_flash_write0
#endif
#ifdef flash_copy
#undef flash_copy
#define flash_copy __real_flash_copy
#endif
size_t flash_copy(struct flash_dev *dev, uint32_t dst, uint32_t src, size_t len)
{
if (!dev)
@ -134,6 +169,16 @@ size_t flash_copy(struct flash_dev *dev, uint32_t dst, uint32_t src, size_t len)
return dev->ops->copy(dev, dst, src, len);
}
#ifdef flash_copy
#undef flash_copy
#define flash_copy __wrap_flash_copy
#endif
#ifdef flash_is_erased
#undef flash_is_erased
#define flash_is_erased __real_flash_is_erased
#endif
int flash_is_erased(struct flash_dev *dev, uint32_t addr, size_t len)
{
if (!dev)
@ -149,6 +194,11 @@ int flash_is_erased(struct flash_dev *dev, uint32_t addr, size_t len)
return 1;
}
#ifdef flash_is_erased
#undef flash_is_erased
#define flash_is_erased __wrap_flash_is_erased
#endif
int flash_erase(struct flash_dev *dev, uint32_t addr, size_t len)
{
if (!dev)

@ -32,7 +32,7 @@ static struct flash_ops stdio_flash_ops = {
.read = stdio_flash_read,
.write = stdio_flash_write,
.copy = default_flash_copy,
.is_erased = default_flash_is_erased,
.is_erased = default_flash_is_one,
.erase = stdio_flash_erase,
};

@ -44,11 +44,41 @@ size_t ftl_read(struct ftl_map *map, void *data, size_t len, uint32_t va)
len);
}
static int ftl_start_transaction(struct ftl_map *map, uint32_t va)
{
uint32_t page;
int ret;
if ((map->outstanding.flags & FTL_PRESENT) && map->outstanding.va == va)
return 0;
if (ftl_sync(map) < 0)
return -1;
if ((ret = trace_path(map, NULL, &page, va)) < 0 && ret != -ERR_NOT_FOUND)
return -1;
if (prepare_head(map) < 0)
return -1;
map->outstanding.flags |= FTL_PRESENT;
map->outstanding.va = va;
map->outstanding.page = page;
if (ret == -ERR_NOT_FOUND) {
map->outstanding.flags |= FTL_NO_MAPPING;
++map->nused_pages;
}
map->offset = 0;
return 0;
}
static int ftl_seek(struct ftl_map *map, uint32_t va)
{
size_t len;
int ret;
uint32_t offset, page, dst, src;
uint32_t offset, dst, src;
if (va >= ftl_get_capacity(map))
return -1;
@ -56,12 +86,7 @@ static int ftl_seek(struct ftl_map *map, uint32_t va)
offset = va & ((1 << map->log2_page_size) - 1);
va >>= map->log2_page_size;
if ((map->flags & FTL_CACHED_VA) && map->last_va != va &&
ftl_sync(map) < 0)
return -1;
if ((ret = trace_path(map, NULL, &page, va)) < 0 &&
ret != -ERR_NOT_FOUND)
if (ftl_start_transaction(map, va) < 0)
return -1;
if (offset < map->offset)
@ -70,19 +95,11 @@ static int ftl_seek(struct ftl_map *map, uint32_t va)
if (offset == map->offset)
return 0;
if (map->offset == 0) {
if (prepare_head(map) < 0)
return -1;
if (ret == -ERR_NOT_FOUND)
++map->nused_pages;
}
dst = map->head << map->log2_page_size;
src = page << map->log2_page_size;
src = map->outstanding.page << map->log2_page_size;
len = offset - map->offset;
if (ret == -ERR_NOT_FOUND)
if (map->outstanding.flags & FTL_NO_MAPPING)
flash_write0(map->dev, dst, len);
else
flash_copy(map->dev, dst, src, len);
@ -107,7 +124,6 @@ size_t ftl_write(struct ftl_map *map, uint32_t va, const void *udata,
return 0;
map->offset += len;
map->flags |= FTL_DIRTY;
return len;
}
@ -117,31 +133,31 @@ int ftl_sync(struct ftl_map *map)
struct ftl_page_desc page_desc;
size_t len;
int ret;
uint32_t page, dst, src;
uint32_t dst, src;
if (!(map->flags & FTL_DIRTY))
if (!(map->outstanding.flags & FTL_PRESENT))
return 0;
if ((ret = trace_path(map, &page_desc, &page, map->last_va)) < 0 &&
ret != -ERR_NOT_FOUND)
return -1;
dst = (map->head << map->log2_page_size) + map->offset;
src = (page << map->log2_page_size) + map->offset;
src = (map->outstanding.page << map->log2_page_size) + map->offset;
len = (1 << map->log2_page_size) - map->offset;
if (map->flags & FTL_UNMAPPED)
if (map->outstanding.flags & FTL_NO_MAPPING)
flash_write0(map->dev, dst, len);
else
flash_copy(map->dev, dst, src, len);
page_desc.nused_pages = map->nused_pages;
if ((ret = trace_path(map, &page_desc, NULL, map->outstanding.va)) < 0 &&
ret != -ERR_NOT_FOUND)
return -1;
if (write_page_desc(map, &page_desc) < 0)
return -1;
map->offset = 0;
map->flags &= ~(FTL_CACHED_VA | FTL_UNMAPPED | FTL_DIRTY);
map->outstanding.flags = 0;
return 0;
}
@ -163,8 +179,6 @@ int ftl_trim(struct ftl_map *map, uint32_t va)
return ret;
}
--map->nused_pages;
for (i = 0; i < 32; ++i) {
level = 32 - i - 1;
@ -174,6 +188,7 @@ int ftl_trim(struct ftl_map *map, uint32_t va)
if (i == 32) {
map->root = UINT32_MAX;
map->nused_pages = 0;
memset(&page_desc, 0xff, sizeof page_desc);
page_desc.nused_pages = 0;
@ -184,6 +199,8 @@ int ftl_trim(struct ftl_map *map, uint32_t va)
if (read_page_desc(map, &alt_page_desc, alt_page) < 0)
return -1;
--map->nused_pages;
page_desc.va = alt_page_desc.va;
page_desc.nused_pages = map->nused_pages;
page_desc.subtrees[level] = UINT32_MAX;

@ -114,6 +114,11 @@ static int free_tail(struct ftl_map *map)
* nothing to be done. Otherwise, we free the tail if necessary and erase the
* block for writing.
*/
#ifdef prepare_head
#undef prepare_head
#define prepare_head __real_prepare_head
#endif
int prepare_head(struct ftl_map *map)
{
size_t log2_pages_per_block = map->log2_pages_per_group +

@ -12,9 +12,14 @@
/* Given the group number, this function checks if a page group is erased by
* checking if the pages that compose the page group are erased.
*/
static int is_group_erased(struct ftl_map *map, uint32_t group)
#ifdef is_group_erased
#undef is_group_erased
#define is_group_erased __real_is_group_erased
#endif
int is_group_erased(struct ftl_map *map, uint32_t group)
{
char data[32];
uint8_t data[32];
struct flash_dev *dev = map->dev;
uint32_t addr = group << (map->log2_pages_per_group + map->log2_page_size);
size_t i, nbytes, len = 1 << (map->log2_pages_per_group + map->log2_page_size);
@ -37,6 +42,13 @@ static int is_group_erased(struct ftl_map *map, uint32_t group)
return 1;
}
#ifdef is_group_erased
#undef is_group_erased
#define is_group_erased __wrap_is_group_erased
#endif
int __wrap_is_group_erased(struct ftl_map *map, uint32_t group);
/* Given the current user page, this function computes the page number of the
* next user page by incrementing the page number. However, if incrementing the
* page number results in the page number of a page containing page
@ -46,7 +58,7 @@ static int is_group_erased(struct ftl_map *map, uint32_t group)
* possible pages on the devices, the page number of the very first user page
* is returned instead.
*/
static uint32_t next_upage(struct ftl_map *map, uint32_t p)
uint32_t next_upage(struct ftl_map *map, uint32_t p)
{
size_t log2_pages_per_block = map->log2_pages_per_group +
map->log2_groups_per_block;
@ -75,6 +87,9 @@ int read_page_group(struct ftl_map *map,
if (flash_read(map->dev, addr, group, sizeof *group) == 0)
return -1;
if (memcmp(group->magic, "FTL", 3) != 0)
return -1;
return 0;
}
@ -82,16 +97,25 @@ int read_page_group(struct ftl_map *map,
* with the user page by locating the footer and more specifically the page
* descriptor within the page group.
*/
#ifdef read_page_desc
#undef read_page_desc
#define read_page_desc __real_read_page_desc
#endif
int read_page_desc(struct ftl_map *map,
struct ftl_page_desc *page_desc, uint32_t upage)
{
uint32_t addr, offset;
addr = align(upage, map->log2_pages_per_group) +
(1 << map->log2_pages_per_group) - 1;
if (addr == upage)
return -1;
addr <<= map->log2_page_size;
offset = sizeof(struct ftl_page_group) +
BIT_MASK(upage, map->log2_pages_per_group) * sizeof *page_desc;
upage = align(upage, map->log2_pages_per_group) +
(1 << map->log2_pages_per_group) - 1;
addr = upage << map->log2_page_size;
if (flash_read(map->dev, addr + offset, page_desc, sizeof *page_desc) == 0)
return -1;
@ -102,9 +126,19 @@ int read_page_desc(struct ftl_map *map,
return 0;
}
#ifdef read_page_desc
#undef read_page_desc
#define read_page_desc __wrap_read_page_desc
#endif
/* Writes the page descriptor to the footer of the current page group and
* increments the head to point to the next free user page.
*/
#ifdef write_page_desc
#undef write_page_desc
#define write_page_desc __real_write_page_desc
#endif
int write_page_desc(struct ftl_map *map,
struct ftl_page_desc *page_desc)
{
@ -144,6 +178,11 @@ int write_page_desc(struct ftl_map *map,
return 0;
}
#ifdef write_page_desc
#undef write_page_desc
#define write_page_desc __wrap_write_page_desc
#endif
/* Prepares the head for writing, writes the user page to the current available
* user page and finally writes the page descriptor to the footer of the page
* group, whereupon the head is incremented to point to the next available user
@ -170,7 +209,7 @@ int write_upage(struct ftl_map *map, const void *page,
* can also be determined, as a single page group may not cover a whole erase
* block.
*/
static int find_block_div(struct ftl_map *map)
int find_block_div(struct ftl_map *map)
{
size_t log2_pages_per_block = map->log2_block_size - map->log2_page_size;
size_t nbytes_avail = (1 << map->log2_page_size) -
@ -198,7 +237,12 @@ static int find_block_div(struct ftl_map *map)
* use, as a block can only be erased as a whole. Therefore, if the first page
* group is not in use, neither will the other page groups in a block.
*/
static int find_block(struct ftl_map *map, struct ftl_page_group *group,
#ifdef find_block
#undef find_block
#define find_block __real_find_block
#endif
int find_block(struct ftl_map *map, struct ftl_page_group *group,
uint32_t *where, uint32_t block)
{
uint32_t page;
@ -223,43 +267,46 @@ static int find_block(struct ftl_map *map, struct ftl_page_group *group,
return -1;
}
#ifdef find_block
#undef find_block
#define find_block __wrap_find_block
#endif
int find_block(struct ftl_map *map, struct ftl_page_group *group,
uint32_t *where, uint32_t block);
/* Given the block number of the first block, attempts to use binary search to
* find the last block that is in use.
*/
static uint32_t find_last_block(struct ftl_map *map, uint32_t first)
uint32_t find_last_block(struct ftl_map *map, uint32_t first)
{
struct ftl_page_group group;
uint32_t mid, low = first, high = map->nblocks - 1;
uint32_t found, next;
while (low <= high) {
while (low < high) {
mid = (low + high) / 2;
if (find_block(map, &group, &found, mid) < 0 ||
group.epoch != map->epoch) {
if (!mid)
return first;
high = mid - 1;
continue;
}
if (((found + 1) > map->nblocks) ||
find_block(map, &group, &next, found + 1) < 0 ||
if (find_block(map, &group, &next, found + 1) < 0 ||
group.epoch != map->epoch)
return found;
low = next;
}
return first;
return low;
}
/* Attempts to find the last page group that is in use within a block by
* performing a binary search on the page groups.
*/
static uint32_t find_last_group(struct ftl_map *map, uint32_t block)
uint32_t find_last_group(struct ftl_map *map, uint32_t block)
{
uint32_t ngroups = UINT32_C(1) << map->log2_groups_per_block;
uint32_t mid, low = 0, high = ngroups - 1;
@ -267,7 +314,7 @@ static uint32_t find_last_group(struct ftl_map *map, uint32_t block)
low += block << map->log2_groups_per_block;
high += block << map->log2_groups_per_block;
while (low <= high) {
while (low < high) {
mid = (low + high) / 2;
if (is_group_erased(map, mid)) {
@ -281,10 +328,10 @@ static uint32_t find_last_group(struct ftl_map *map, uint32_t block)
low = mid + 1;
}
return block << map->log2_groups_per_block;
return low;
}
static int find_root(struct ftl_map *map, uint32_t group)
int find_root(struct ftl_map *map, uint32_t group)
{
struct ftl_page_desc page_desc;
uint32_t upage;
@ -306,7 +353,7 @@ static int find_root(struct ftl_map *map, uint32_t group)
* within the page group, the first user page of the next page group should be
* used as that page group should not be in use.
*/
static int find_head(struct ftl_map *map)
int find_head(struct ftl_map *map)
{
size_t log2_pages_per_block = map->log2_pages_per_group +
map->log2_groups_per_block;
@ -333,9 +380,8 @@ static void reset_map(struct ftl_map *map)
map->nblocks = flash_get_size(map->dev) >> map->log2_block_size;
map->flags = 0;
map->last_va = 0;
map->last_pa = 0;
memset(&map->outstanding, 0, sizeof map->outstanding);
map->offset = 0;
map->head = 0;
map->tail = 0;
@ -400,6 +446,11 @@ int ftl_resume_map(struct ftl_map *map)
* depth until we have either found that there is no further subtree to
* traverse or until we have found the actual user page.
*/
#ifdef trace_path
#undef trace_path
#define trace_path __real_trace_path
#endif
int trace_path(struct ftl_map *map, struct ftl_page_desc *new_page_desc,
uint32_t *page, uint32_t va)
{
@ -407,21 +458,6 @@ int trace_path(struct ftl_map *map, struct ftl_page_desc *new_page_desc,
uint8_t depth = 0;
uint32_t upage = map->root;
#if 0
/* FIXME */
if (!new_page_desc && (map->flags & FTL_CACHED_VA) && map->last_va == va) {
if (map->flags & FTL_UNMAPPED)
return -1;
if (page)
*page = map->last_pa;
return 0;
}
#endif
map->flags &= ~(FTL_CACHED_VA | FTL_UNMAPPED);
if (new_page_desc)
new_page_desc->va = va;
@ -457,10 +493,6 @@ int trace_path(struct ftl_map *map, struct ftl_page_desc *new_page_desc,
if (page)
*page = upage;
map->flags |= FTL_CACHED_VA;
map->last_va = va;
map->last_pa = upage;
return 0;
err_not_found:
@ -470,8 +502,5 @@ err_not_found:
}
}
map->flags |= FTL_CACHED_VA | FTL_UNMAPPED;
map->last_va = va;
return -ERR_NOT_FOUND;
}

@ -75,8 +75,8 @@ long mufs_seek(struct mufs_file *file, long offset, int whence)
switch (whence) {
case SEEK_SET: break;
case SEEK_CUR: offset = file->va + offset; break;
case SEEK_END: offset = file->tree->file_size; break;
case SEEK_CUR: offset += file->va; break;
case SEEK_END: offset += file->tree->file_size; break;
default: return -1;
}

@ -278,6 +278,7 @@ static size_t mufs_do_write(struct mufs_tree *tree,
{
struct mufs *fs = tree->fs;
struct flash_dev *dev = fs->dev;
size_t ret;
uint32_t page, offset;
offset = va & ((UINT32_C(1) << dev->log2_block_size) - 1);
@ -287,8 +288,16 @@ static size_t mufs_do_write(struct mufs_tree *tree,
if (mufs_alloc_page(tree, &page, va) < 0)
return 0;
return flash_write(dev, (page << dev->log2_block_size) + offset, data,
ret = flash_write(dev, (page << dev->log2_block_size) + offset, data,
len);
if (ret == 0)
return 0;
if (flash_sync(dev) < 0)
return 0;
return ret;
}
size_t mufs_tree_write(struct mufs_tree *tree, void *data,
@ -326,5 +335,5 @@ int mufs_sync_tree(struct mufs_tree *tree)
if (flash_write(dev, tree->va, &dtree, sizeof dtree) == 0)
return -1;
return 0;
return flash_sync(dev);
}

@ -1,7 +1 @@
ifeq ($(TARGET),host)
else
obj-y += source/platform/spi_flash.o
obj-y += source/platform/usart.o
endif
-include source/platform/$(TARGET)/Makefile

@ -1,3 +1,6 @@
obj-y += source/platform/spi_flash.o
obj-y += source/platform/usart.o
obj-y += source/platform/stm32f0/gpio.o
obj-y += source/platform/stm32f0/rcc.o
obj-y += source/platform/stm32f0/rtc.o

@ -1,3 +1,6 @@
obj-y += source/platform/spi_flash.o
obj-y += source/platform/usart.o
obj-y += source/platform/stm32f1/gpio.o
obj-y += source/platform/stm32f1/rcc.o
obj-y += source/platform/stm32f1/rtc.o

@ -5,5 +5,6 @@ obj-y += source/shell/echo.o
obj-y += source/shell/flash.o
obj-y += source/shell/ftl.o
obj-y += source/shell/mufs.o
obj-y += source/shell/progress.o
obj-y += source/shell/rtc.o
obj-y += source/shell/version.o

@ -15,6 +15,7 @@
#include <shell.h>
#include <shell/mufs.h>
#include <shell/progress.h>
extern struct flash_dev *flash;
struct mufs *mufs = NULL;
@ -144,12 +145,12 @@ int shell_stat(struct console *con, size_t argc, const char **argv)
}
switch (stat.type) {
case MUFS_DIR: printf(" type: directory\n"); break;
case MUFS_FILE: printf(" type: file\n"); break;
case MUFS_DIR: fprintf(con->fp, " type: directory\n"); break;
case MUFS_FILE: fprintf(con->fp, " type: file\n"); break;
default: return -1;
}
printf(" file size: %" PRIu32 " bytes\n", stat.file_size);
fprintf(con->fp, " file size: %" PRIu32 " bytes\n", stat.file_size);
return 0;
}
@ -274,6 +275,76 @@ int shell_mv(struct console *con, size_t argc, const char **argv)
return 0;
}
int shell_cp(struct console *con, size_t argc, const char **argv)
{
struct mufs_stat stat;
struct mufs_file *in, *out;
char data[256];
size_t ret;
size_t count = 0;
size_t total;
if (!mufs) {
fprintf(con->fp, "error: no file system mounted.\n");
return -1;
}
if (argc < 2) {
fprintf(con->fp, "usage: mufs cp <src> <dst>\n");
return -1;
}
if (mufs_stat(mufs, argv[0], &stat) < 0) {
fprintf(con->fp, "error: unable to stat the file\n");
return -1;
}
if (stat.type != MUFS_FILE) {
fprintf(con->fp, "error: only able to copy files\n");
return -1;
}
total = stat.file_size;
if (!(in = mufs_open(mufs, argv[0], MUFS_READ))) {
fprintf(con->fp, "error: unable to open the file\n");
return -1;
}
if (!(out = mufs_open(mufs, argv[1], MUFS_WRITE))) {
fprintf(con->fp, "error: unable to open the file\n");
goto err_close_in;
}
draw_progress(con->fp, count, total);
while (count < total) {
ret = mufs_read(in, data, sizeof data);
if (ret == 0)
break;
ret = mufs_write(out, data, ret);
if (ret == 0)
break;
count += ret;
draw_progress(con->fp, count, total);
}
fputc('\n', con->fp);
mufs_close(out);
mufs_close(in);
return 0;
err_close_in:
mufs_close(in);
return -1;
}
int shell_rm(struct console *con, size_t argc, const char **argv)
{
if (argc < 1) {
@ -334,6 +405,7 @@ static struct cmd mufs_cmds[] = {
{ "write", NULL, shell_write },
{ "append", NULL, shell_append },
{ "mv", NULL, shell_mv, },
{ "cp", NULL, shell_cp, },
{ "rm", NULL, shell_rm, },
{ NULL, NULL, NULL },
};

@ -0,0 +1,26 @@
#include <stdio.h>
#define TERM_WIDTH 80
void draw_progress(FILE *fp, unsigned long pos, unsigned long max)
{
unsigned long head, i;
if (pos > max)
pos = max;
head = (TERM_WIDTH - 2) * pos / max;
fputc('[', fp);
for (i = 0; i < head; ++i)
fputc('#', fp);
for (; i < (TERM_WIDTH - 2); ++i)
fputc(' ', fp);
fputc(']', fp);
for (i = 0; i < TERM_WIDTH; ++i)
fputc('\b', fp);
}

@ -0,0 +1,34 @@
test-obj-y += source/tests/main.o
test-obj-y += source/tests/flash/mock.o
test-obj-y += source/tests/ftl/main.o
test-obj-y += source/tests/ftl/mock.o
test-obj-y += source/tests/ftl/find_block.o
test-obj-y += source/tests/ftl/find_block_div.o
test-obj-y += source/tests/ftl/find_last_block.o
test-obj-y += source/tests/ftl/find_last_group.o
test-obj-y += source/tests/ftl/find_head.o
test-obj-y += source/tests/ftl/find_root.o
test-obj-y += source/tests/ftl/next_upage.o
test-obj-y += source/tests/ftl/read_page_group.o
test-obj-y += source/tests/ftl/read_page_desc.o
test-obj-y += source/tests/ftl/write_page_desc.o
test-obj-y += source/tests/ftl/write_upage.o
test-obj-y += source/tests/ftl/trace_path.o
test-obj-y += source/tests/ftl/ftl_is_mapped.o
test-obj-y += source/tests/ftl/ftl_read.o
TEST_CFLAGS += -Dflash_read=__wrap_flash_read
TEST_CFLAGS += -Dflash_write=__wrap_flash_write
TEST_CFLAGS += -Dflash_write0=__wrap_flash_write0
TEST_CFLAGS += -Dflash_copy=__wrap_flash_copy
TEST_CFLAGS += -Dflash_is_erased=__wrap_flash_is_erased
TEST_CFLAGS += -Dis_group_erased=__wrap_is_group_erased
TEST_CFLAGS += -Dfind_block=__wrap_find_block
TEST_CFLAGS += -Dprepare_head=__wrap_prepare_head
TEST_CFLAGS += -Dread_page_desc=__wrap_read_page_desc
TEST_CFLAGS += -Dwrite_page_desc=__wrap_write_page_desc
TEST_CFLAGS += -Dtrace_path=__wrap_trace_path

@ -0,0 +1,89 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
size_t __wrap_flash_read(struct flash_dev *dev, uint32_t addr,
void *data, size_t len)
{
const void *ret_data;
size_t ret_len;
(void)dev;
check_expected(addr);
check_expected(len);
ret_len = mock_type(size_t);
ret_data = mock_type(const void *);
if (len > ret_len)
len = ret_len;
if (len && ret_data)
memcpy(data, ret_data, len);
return len;
}
size_t __wrap_flash_write(struct flash_dev *dev, uint32_t addr,
const void *data, size_t len)
{
void *ret_data;
size_t ret_len;
(void)dev;
check_expected(addr);
check_expected(len);
ret_len = mock_type(size_t);
ret_data = mock_type(void *);
if (len > ret_len)
len = ret_len;
if (len)
memcpy(ret_data, data, len);
return len;
}
size_t __wrap_flash_write0(struct flash_dev *dev, uint32_t addr, size_t len)
{
(void)dev;
check_expected(addr);
check_expected(len);
return mock_type(int);
}
size_t __wrap_flash_copy(struct flash_dev *dev, uint32_t dst, uint32_t src, size_t len)
{
(void)dev;
check_expected(dst);
check_expected(src);
check_expected(len);
return mock_type(int);
}
int __wrap_flash_is_erased(struct flash_dev *dev, uint32_t addr, size_t len)
{
(void)dev;
check_expected(addr);
check_expected(len);
return mock_type(int);
}

@ -0,0 +1,170 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
int __real_find_block(struct ftl_map *map, struct ftl_page_group *group,
uint32_t *where, uint32_t block);
static void test_block0(void **state)
{
struct ftl_map map;
struct ftl_page_group group, ret_group;
int ret;
uint32_t where;
(void)state;
memcpy(group.magic, "FTL", 3);
map.log2_block_size = ilog2(64 * KIB);
map.log2_page_size = ilog2(1 * KIB);
map.log2_pages_per_group = ilog2(8);
map.nblocks = 64;
expect_value(__wrap_flash_read, addr, (8 - 1) * 1 * KIB);
expect_value(__wrap_flash_read, len, sizeof group);
will_return(__wrap_flash_read, sizeof group);
will_return(__wrap_flash_read, &group);
ret = __real_find_block(&map, &ret_group, &where, 0);
assert_int_equal(ret, 0);
assert_int_equal(where, 0);
}
static void test_block3(void **state)
{
struct ftl_map map;
struct ftl_page_group group, ret_group;
int ret;
uint32_t where;
(void)state;
memcpy(group.magic, "FTL", 3);
map.log2_block_size = ilog2(64 * KIB);
map.log2_page_size = ilog2(1 * KIB);
map.log2_pages_per_group = ilog2(8);
map.nblocks = 64;
expect_value(__wrap_flash_read, addr, (8 - 1) * 1 * KIB);
expect_value(__wrap_flash_read, len, sizeof group);
will_return(__wrap_flash_read, 0);
will_return(__wrap_flash_read, NULL);
expect_value(__wrap_flash_read, addr, (1 * 64 + 8 - 1) * 1 * KIB);
expect_value(__wrap_flash_read, len, sizeof group);
will_return(__wrap_flash_read, 0);
will_return(__wrap_flash_read, NULL);
expect_value(__wrap_flash_read, addr, (2 * 64 + 8 - 1) * 1 * KIB);
expect_value(__wrap_flash_read, len, sizeof group);
will_return(__wrap_flash_read, 0);
will_return(__wrap_flash_read, NULL);
expect_value(__wrap_flash_read, addr, (3 * 64 + 8 - 1) * 1 * KIB);
expect_value(__wrap_flash_read, len, sizeof group);
will_return(__wrap_flash_read, sizeof group);
will_return(__wrap_flash_read, &group);
ret = __real_find_block(&map, &ret_group, &where, 0);
assert_int_equal(ret, 0);
assert_int_equal(where, 3);
}
static void test_block8_offset5(void **state)
{
struct ftl_map map;
struct ftl_page_group group, ret_group;
int ret;
uint32_t where;
(void)state;
memcpy(group.magic, "FTL", 3);
map.log2_block_size = ilog2(64 * KIB);
map.log2_page_size = ilog2(1 * KIB);
map.log2_pages_per_group = ilog2(8);
map.nblocks = 64;
expect_value(__wrap_flash_read, addr, (5 * 64 + 8 - 1) * 1 * KIB);
expect_value(__wrap_flash_read, len, sizeof group);
will_return(__wrap_flash_read, 0);
will_return(__wrap_flash_read, NULL);
expect_value(__wrap_flash_read, addr, (6 * 64 + 8 - 1) * 1 * KIB);
expect_value(__wrap_flash_read, len, sizeof group);
will_return(__wrap_flash_read, 0);
will_return(__wrap_flash_read, NULL);
expect_value(__wrap_flash_read, addr, (7 * 64 + 8 - 1) * 1 * KIB);
expect_value(__wrap_flash_read, len, sizeof group);
will_return(__wrap_flash_read, 0);
will_return(__wrap_flash_read, NULL);
expect_value(__wrap_flash_read, addr, (8 * 64 + 8 - 1) * 1 * KIB);
expect_value(__wrap_flash_read, len, sizeof group);
will_return(__wrap_flash_read, sizeof group);
will_return(__wrap_flash_read, &group);
ret = __real_find_block(&map, &ret_group, &where, 5);
assert_int_equal(ret, 0);
assert_int_equal(where, 8);
}
static void test_no_block(void **state)
{
struct ftl_map map;
struct ftl_page_group group, ret_group;
int ret;
uint32_t where;
(void)state;
memcpy(group.magic, "FTL", 3);
map.log2_block_size = ilog2(64 * KIB);
map.log2_page_size = ilog2(1 * KIB);
map.log2_pages_per_group = ilog2(8);
map.nblocks = 64;
expect_value(__wrap_flash_read, addr, (61 * 64 + 8 - 1) * 1 * KIB);
expect_value(__wrap_flash_read, len, sizeof group);
will_return(__wrap_flash_read, 0);
will_return(__wrap_flash_read, NULL);
expect_value(__wrap_flash_read, addr, (62 * 64 + 8 - 1) * 1 * KIB);
expect_value(__wrap_flash_read, len, sizeof group);
will_return(__wrap_flash_read, 0);
will_return(__wrap_flash_read, NULL);
expect_value(__wrap_flash_read, addr, (63 * 64 + 8 - 1) * 1 * KIB);
expect_value(__wrap_flash_read, len, sizeof group);
will_return(__wrap_flash_read, 0);
will_return(__wrap_flash_read, NULL);
ret = __real_find_block(&map, &ret_group, &where, 61);
assert_int_equal(ret, -1);
}
int test_find_block(void)
{
const struct CMUnitTest tests[] = {
{ "find_block: block=0, offset=0", test_block0, NULL, NULL, NULL },
{ "find_block: block=3, offset=0", test_block3, NULL, NULL, NULL },
{ "find_block: block=8, offset=5", test_block8_offset5, NULL, NULL, NULL },
{ "find_block: no block, offset=61", test_no_block, NULL, NULL, NULL },
};
return cmocka_run_group_tests_name("find_block", tests, NULL, NULL);
}

@ -0,0 +1,70 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
int find_block_div(struct ftl_map *map);
static void test_4k_page_64k_block(void **state)
{
struct ftl_map map;
(void)state;
map.log2_block_size = ilog2(64 * KIB);
map.log2_page_size = ilog2(4 * KIB);
find_block_div(&map);
assert_int_equal(1 << map.log2_pages_per_group, 16);
assert_int_equal(1 << map.log2_groups_per_block, 1);
}
static void test_4k_page_16k_block(void **state)
{
struct ftl_map map;
(void)state;
map.log2_block_size = ilog2(16 * KIB);
map.log2_page_size = ilog2(4 * KIB);
find_block_div(&map);
assert_int_equal(1 << map.log2_pages_per_group, 4);
assert_int_equal(1 << map.log2_groups_per_block, 1);
}
static void test_1k_page_64k_block(void **state)
{
struct ftl_map map;
(void)state;
map.log2_block_size = ilog2(64 * KIB);
map.log2_page_size = ilog2(1 * KIB);
find_block_div(&map);
assert_int_equal(1 << map.log2_pages_per_group, 8);
assert_int_equal(1 << map.log2_groups_per_block, 8);
}
int test_find_block_div(void)
{
const struct CMUnitTest tests[] = {
{ "find_block_div: 4K page, 64K block", test_4k_page_64k_block, NULL, NULL, NULL },
{ "find_block_div: 4K page, 16K block", test_4k_page_16k_block, NULL, NULL, NULL },
{ "find_block_div: 1K page, 64K block", test_1k_page_64k_block, NULL, NULL, NULL },
};
return cmocka_run_group_tests_name("find_block_div", tests, NULL, NULL);
}

@ -0,0 +1,104 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
int find_head(struct ftl_map *map);
static void test_erased(void **state)
{
struct ftl_map map;
int ret;
(void)state;
map.log2_pages_per_group = ilog2(16);
map.log2_groups_per_block = ilog2(1);
map.nblocks = 4;
map.root = 4;
expect_value(__wrap_flash_is_erased, addr, 5);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 1);
ret = find_head(&map);
assert_int_equal(ret, 0);
assert_int_equal(map.head, 5);
}
static void test_end_of_block(void **state)
{
struct ftl_map map;
int ret;
(void)state;
map.log2_pages_per_group = ilog2(16);
map.log2_groups_per_block = ilog2(1);
map.nblocks = 4;
map.root = 4;
expect_value(__wrap_flash_is_erased, addr, 5);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 0);
expect_value(__wrap_flash_is_erased, addr, 6);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 0);
expect_value(__wrap_flash_is_erased, addr, 7);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 0);
expect_value(__wrap_flash_is_erased, addr, 8);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 0);
expect_value(__wrap_flash_is_erased, addr, 9);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 0);
expect_value(__wrap_flash_is_erased, addr, 10);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 0);
expect_value(__wrap_flash_is_erased, addr, 11);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 0);
expect_value(__wrap_flash_is_erased, addr, 12);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 0);
expect_value(__wrap_flash_is_erased, addr, 13);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 0);
expect_value(__wrap_flash_is_erased, addr, 14);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 0);
ret = find_head(&map);
assert_int_equal(ret, 0);
assert_int_equal(map.head, 16);
}
int test_find_head(void)
{
const struct CMUnitTest tests[] = {
{ "find_head: erased", test_erased, NULL, NULL, NULL },
{ "find_head: end of block", test_end_of_block, NULL, NULL, NULL },
};
return cmocka_run_group_tests_name("find_head", tests, NULL, NULL);
}

@ -0,0 +1,291 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
uint32_t find_last_block(struct ftl_map *map, uint32_t first);
static void test_middle(void **state)
{
struct ftl_map map;
struct ftl_page_group group;
uint32_t ret;
(void)state;
memcpy(group.magic, "FTL", 3);
group.epoch = 0;
map.nblocks = 64;
map.epoch = 0;
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 31);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 31);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 32);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 0);
will_return(__wrap_find_block, -1);
ret = find_last_block(&map, 0);
assert_int_equal(ret, 31);
}
static void test_first(void **state)
{
struct ftl_map map;
struct ftl_page_group group;
uint32_t ret;
(void)state;
memcpy(group.magic, "FTL", 3);
group.epoch = 0;
map.nblocks = 64;
map.epoch = 0;
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 31);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 0);
will_return(__wrap_find_block, -1);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 15);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 0);
will_return(__wrap_find_block, -1);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 7);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 0);
will_return(__wrap_find_block, -1);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 3);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 0);
will_return(__wrap_find_block, -1);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 1);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 0);
will_return(__wrap_find_block, -1);
ret = find_last_block(&map, 0);
assert_int_equal(ret, 0);
}
static void test_last(void **state)
{
struct ftl_map map;
struct ftl_page_group group;
uint32_t ret;
(void)state;
memcpy(group.magic, "FTL", 3);
group.epoch = 0;
map.nblocks = 64;
map.epoch = 0;
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 31);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 31);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 32);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 32);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 47);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 47);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 48);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 48);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 55);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 55);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 56);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 56);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 59);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 59);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 60);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 60);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 61);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 61);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 62);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 62);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 62);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 62);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 63);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 63);
will_return(__wrap_find_block, 0);
ret = find_last_block(&map, 0);
assert_int_equal(ret, 63);
}
static void test_left_right(void **state)
{
struct ftl_map map;
struct ftl_page_group group;
uint32_t ret;
(void)state;
memcpy(group.magic, "FTL", 3);
group.epoch = 0;
map.nblocks = 64;
map.epoch = 0;
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 31);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 31);
will_return(__wrap_find_block, -1);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 15);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 15);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 16);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 16);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 23);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 23);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 24);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 24);
will_return(__wrap_find_block, -1);
ret = find_last_block(&map, 0);
assert_int_equal(ret, 23);
}
static void test_right_left(void **state)
{
struct ftl_map map;
struct ftl_page_group group;
uint32_t ret;
(void)state;
memcpy(group.magic, "FTL", 3);
group.epoch = 0;
map.nblocks = 64;
map.epoch = 0;
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 31);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 31);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 32);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 32);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 47);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 47);
will_return(__wrap_find_block, -1);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 39);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 39);
will_return(__wrap_find_block, 0);
expect_value(__wrap_find_block, map, &map);
expect_value(__wrap_find_block, block, 40);
will_return(__wrap_find_block, &group);
will_return(__wrap_find_block, 40);
will_return(__wrap_find_block, -1);
ret = find_last_block(&map, 0);
assert_int_equal(ret, 39);
}
int test_find_last_block(void)
{
const struct CMUnitTest tests[] = {
{ "find_last_block: middle", test_middle, NULL, NULL, NULL },
{ "find_last_block: first", test_first, NULL, NULL, NULL },
{ "find_last_block: last", test_last, NULL, NULL, NULL },
{ "find_last_block: left right", test_left_right, NULL, NULL, NULL },
{ "find_last_block: right left", test_right_left, NULL, NULL, NULL },
};
return cmocka_run_group_tests_name("find_last_block", tests, NULL, NULL);
}

@ -0,0 +1,156 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
uint32_t find_last_group(struct ftl_map *map, uint32_t block);
static void test_middle(void **state)
{
struct ftl_map map;
uint32_t ret;
(void)state;
map.log2_groups_per_block = ilog2(8);
expect_value(__wrap_is_group_erased, map, &map);
expect_value(__wrap_is_group_erased, group, 32 + 3);
will_return(__wrap_is_group_erased, 0);
expect_value(__wrap_is_group_erased, map, &map);
expect_value(__wrap_is_group_erased, group, 32 + 4);
will_return(__wrap_is_group_erased, 1);
ret = find_last_group(&map, 4);
assert_int_equal(ret, 32 + 3);
}
static void test_first(void **state)
{
struct ftl_map map;
uint32_t ret;
(void)state;
map.log2_groups_per_block = ilog2(8);
expect_value(__wrap_is_group_erased, map, &map);
expect_value(__wrap_is_group_erased, group, 32 + 3);
will_return(__wrap_is_group_erased, 1);
expect_value(__wrap_is_group_erased, map, &map);
expect_value(__wrap_is_group_erased, group, 32 + 1);
will_return(__wrap_is_group_erased, 1);
ret = find_last_group(&map, 4);
assert_int_equal(ret, 32);
}
static void test_last(void **state)
{
struct ftl_map map;
uint32_t ret;
(void)state;
map.log2_groups_per_block = ilog2(8);
expect_value(__wrap_is_group_erased, map, &map);
expect_value(__wrap_is_group_erased, group, 32 + 3);
will_return(__wrap_is_group_erased, 0);
expect_value(__wrap_is_group_erased, map, &map);
expect_value(__wrap_is_group_erased, group, 32 + 4);
will_return(__wrap_is_group_erased, 0);
expect_value(__wrap_is_group_erased, map, &map);
expect_value(__wrap_is_group_erased, group, 32 + 5);
will_return(__wrap_is_group_erased, 0);
expect_value(__wrap_is_group_erased, map, &map);
expect_value(__wrap_is_group_erased, group, 32 + 6);
will_return(__wrap_is_group_erased, 0);
expect_value(__wrap_is_group_erased, map, &map);
expect_value(__wrap_is_group_erased, group, 32 + 6);
will_return(__wrap_is_group_erased, 0);
expect_value(__wrap_is_group_erased, map, &map);
expect_value(__wrap_is_group_erased, group, 32 + 7);
will_return(__wrap_is_group_erased, 0);
ret = find_last_group(&map, 4);
assert_int_equal(ret, 32 + 7);
}
static void test_left_right(void **state)
{
struct ftl_map map;
uint32_t ret;
(void)state;
map.log2_groups_per_block = ilog2(8);
expect_value(__wrap_is_group_erased, map, &map);
expect_value(__wrap_is_group_erased, group, 32 + 3);
will_return(__wrap_is_group_erased, 1);
expect_value(__wrap_is_group_erased, map, &map);
expect_value(__wrap_is_group_erased, group, 32 + 1);
will_return(__wrap_is_group_erased, 0);
expect_value(__wrap_is_group_erased, map, &map);
expect_value(__wrap_is_group_erased, group, 32 + 2);
will_return(__wrap_is_group_erased, 0);
ret = find_last_group(&map, 4);
assert_int_equal(ret, 32 + 2);
}
static void test_right_left(void **state)
{
struct ftl_map map;
uint32_t ret;
(void)state;
map.log2_groups_per_block = ilog2(8);
expect_value(__wrap_is_group_erased, map, &map);
expect_value(__wrap_is_group_erased, group, 32 + 3);
will_return(__wrap_is_group_erased, 0);
expect_value(__wrap_is_group_erased, map, &map);
expect_value(__wrap_is_group_erased, group, 32 + 4);
will_return(__wrap_is_group_erased, 0);
expect_value(__wrap_is_group_erased, map, &map);
expect_value(__wrap_is_group_erased, group, 32 + 5);
will_return(__wrap_is_group_erased, 1);
ret = find_last_group(&map, 4);
assert_int_equal(ret, 32 + 4);
}
int test_find_last_group(void)
{
const struct CMUnitTest tests[] = {
{ "find_last_group: middle", test_middle, NULL, NULL, NULL },
{ "find_last_group: first", test_first, NULL, NULL, NULL },
{ "find_last_group: last", test_last, NULL, NULL, NULL },
{ "find_last_group: left right", test_left_right, NULL, NULL, NULL },
{ "find_last_group: right left", test_right_left, NULL, NULL, NULL },
};
return cmocka_run_group_tests_name("find_last_group", tests, NULL, NULL);
}

@ -0,0 +1,153 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
int find_root(struct ftl_map *map, uint32_t group);
static void test_group0_upage0(void **state)
{
struct ftl_map map;
struct ftl_page_desc page_desc;
int ret;
(void)state;
map.log2_pages_per_group = ilog2(16);
map.log2_groups_per_block = ilog2(1);
map.nblocks = 4;
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 0);
will_return(__wrap_read_page_desc, &page_desc);
will_return(__wrap_read_page_desc, -1);
ret = find_root(&map, 0);
assert_int_equal(ret, -1);
}
static void test_group0_upage5(void **state)
{
struct ftl_map map;
struct ftl_page_desc page_desc;
int ret;
(void)state;
map.log2_pages_per_group = ilog2(16);
map.log2_groups_per_block = ilog2(1);
map.nblocks = 4;
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 0);
will_return(__wrap_read_page_desc, &page_desc);
will_return(__wrap_read_page_desc, 0);
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 1);
will_return(__wrap_read_page_desc, &page_desc);
will_return(__wrap_read_page_desc, 0);
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 2);
will_return(__wrap_read_page_desc, &page_desc);
will_return(__wrap_read_page_desc, 0);
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 3);
will_return(__wrap_read_page_desc, &page_desc);
will_return(__wrap_read_page_desc, 0);
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 4);
will_return(__wrap_read_page_desc, &page_desc);
will_return(__wrap_read_page_desc, 0);
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 5);
will_return(__wrap_read_page_desc, &page_desc);
will_return(__wrap_read_page_desc, 0);
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 6);
will_return(__wrap_read_page_desc, &page_desc);
will_return(__wrap_read_page_desc, -1);
ret = find_root(&map, 0);
assert_int_equal(ret, 0);
assert_int_equal(map.root, 5);
}
static void test_group1_upage5(void **state)
{
struct ftl_map map;
struct ftl_page_desc page_desc;
int ret;
(void)state;
map.log2_pages_per_group = ilog2(16);
map.log2_groups_per_block = ilog2(1);
map.nblocks = 4;
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 16);
will_return(__wrap_read_page_desc, &page_desc);
will_return(__wrap_read_page_desc, 0);
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 16 + 1);
will_return(__wrap_read_page_desc, &page_desc);
will_return(__wrap_read_page_desc, 0);
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 16 + 2);
will_return(__wrap_read_page_desc, &page_desc);
will_return(__wrap_read_page_desc, 0);
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 16 + 3);
will_return(__wrap_read_page_desc, &page_desc);
will_return(__wrap_read_page_desc, 0);
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 16 + 4);
will_return(__wrap_read_page_desc, &page_desc);
will_return(__wrap_read_page_desc, 0);
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 16 + 5);
will_return(__wrap_read_page_desc, &page_desc);
will_return(__wrap_read_page_desc, 0);
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 16 + 6);
will_return(__wrap_read_page_desc, &page_desc);
will_return(__wrap_read_page_desc, -1);
ret = find_root(&map, 1);
assert_int_equal(ret, 0);
assert_int_equal(map.root, 16 + 5);
}
int test_find_root(void)
{
const struct CMUnitTest tests[] = {
{ "find_root: group=0, upage=0 (no root)", test_group0_upage0, NULL, NULL, NULL },
{ "find_root: group=0, upage=5", test_group0_upage5, NULL, NULL, NULL },
{ "find_root: group=1, upage=5", test_group1_upage5, NULL, NULL, NULL },
};
return cmocka_run_group_tests_name("find_root", tests, NULL, NULL);
}

@ -0,0 +1,62 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
static void test_unmapped(void **state)
{
struct ftl_map map;
int ret;
(void)state;
expect_value(__wrap_trace_path, map, &map);
expect_value(__wrap_trace_path, new_page_desc, NULL);
expect_value(__wrap_trace_path, page, NULL);
expect_value(__wrap_trace_path, va, 42);
will_return(__wrap_trace_path, NULL);
will_return(__wrap_trace_path, 0);
will_return(__wrap_trace_path, -1);
ret = ftl_is_mapped(&map, 42);
assert_int_equal(ret, 0);
}
static void test_mapped(void **state)
{
struct ftl_map map;
int ret;
(void)state;
expect_value(__wrap_trace_path, map, &map);
expect_value(__wrap_trace_path, new_page_desc, NULL);
expect_value(__wrap_trace_path, page, NULL);
expect_value(__wrap_trace_path, va, 42);
will_return(__wrap_trace_path, NULL);
will_return(__wrap_trace_path, 0);
will_return(__wrap_trace_path, 0);
ret = ftl_is_mapped(&map, 42);
assert_int_equal(ret, 1);
}
int test_ftl_is_mapped(void)
{
const struct CMUnitTest tests[] = {
{ "ftl_is_mapped: unmapped", test_unmapped, NULL, NULL, NULL },
{ "ftl_is_mapped: mapped", test_mapped, NULL, NULL, NULL },
};
return cmocka_run_group_tests_name("ftl_is_unmapped", tests, NULL, NULL);
}

@ -0,0 +1,80 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
static void test_basic_read(void **state)
{
char data[24];
struct ftl_map map;
int ret;
(void)state;
map.log2_page_size = ilog2(4 * KIB);
map.outstanding.flags = 0;
expect_value(__wrap_trace_path, map, &map);
expect_value(__wrap_trace_path, new_page_desc, NULL);
expect_not_value(__wrap_trace_path, page, NULL);
expect_value(__wrap_trace_path, va, 4);
will_return(__wrap_trace_path, NULL);
will_return(__wrap_trace_path, 42);
will_return(__wrap_trace_path, 0);
expect_value(__wrap_flash_read, addr, 42 << 12 | 0x321);
expect_value(__wrap_flash_read, len, 24);
will_return(__wrap_flash_read, 24);
will_return(__wrap_flash_read, NULL);
ret = ftl_read(&map, data, 24, 0x4321);
assert_int_equal(ret, 24);
}
static void test_boundary(void **state)
{
char data[32];
struct ftl_map map;
int ret;
(void)state;
map.log2_page_size = ilog2(4 * KIB);
map.outstanding.flags = 0;
expect_value(__wrap_trace_path, map, &map);
expect_value(__wrap_trace_path, new_page_desc, NULL);
expect_not_value(__wrap_trace_path, page, NULL);
expect_value(__wrap_trace_path, va, 4);
will_return(__wrap_trace_path, NULL);
will_return(__wrap_trace_path, 42);
will_return(__wrap_trace_path, 0);
expect_value(__wrap_flash_read, addr, 42 << 12 | 0xff0);
expect_value(__wrap_flash_read, len, 16);
will_return(__wrap_flash_read, 16);
will_return(__wrap_flash_read, NULL);
ret = ftl_read(&map, data, 32, 0x4ff0);
assert_int_equal(ret, 16);
}
int test_ftl_read(void)
{
const struct CMUnitTest tests[] = {
{ "ftl_read: basic read", test_basic_read, NULL, NULL, NULL },
{ "ftl_read: boundary", test_boundary, NULL, NULL, NULL },
};
return cmocka_run_group_tests_name("ftl_read", tests, NULL, NULL);
}

@ -0,0 +1,51 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
int test_find_block(void);
int test_find_block_div(void);
int test_find_last_block(void);
int test_find_last_group(void);
int test_find_head(void);
int test_find_root(void);
int test_next_upage(void);
int test_read_page_group(void);
int test_read_page_desc(void);
int test_write_page_desc(void);
int test_write_upage(void);
int test_trace_path(void);
int test_ftl_is_mapped(void);
int test_ftl_read(void);
int test_ftl(void)
{
int count = 0;
count += test_find_block();
count += test_find_block_div();
count += test_find_last_block();
count += test_find_last_group();
count += test_find_head();
count += test_find_root();
count += test_next_upage();
count += test_read_page_group();
count += test_read_page_desc();
count += test_write_page_desc();
count += test_write_upage();
count += test_trace_path();
count += test_ftl_is_mapped();
count += test_ftl_read();
return count;
}

@ -0,0 +1,87 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <ftl.h>
int __wrap_is_group_erased(struct ftl_map *map, uint32_t group)
{
check_expected_ptr(map);
check_expected(group);
return mock_type(int);
}
int __wrap_find_block(struct ftl_map *map, struct ftl_page_group *group,
uint32_t *where, uint32_t block)
{
struct ftl_page_group *ret_group;
check_expected_ptr(map);
check_expected(block);
ret_group = mock_type(struct ftl_page_group *);
if (ret_group)
memcpy(group, ret_group, sizeof *group);
*where = mock_type(uint32_t);
return mock_type(int);
}
int __wrap_prepare_head(struct ftl_map *map)
{
(void)map;
return mock_type(int);
}
int __wrap_read_page_desc(struct ftl_map *map,
struct ftl_page_desc *page_desc, uint32_t upage)
{
check_expected_ptr(map);
check_expected(upage);
memcpy(page_desc, mock_type(struct ftl_page_desc *), sizeof *page_desc);
return mock_type(int);
}
int __wrap_write_page_desc(struct ftl_map *map,
struct ftl_page_desc *page_desc)
{
check_expected_ptr(map);
check_expected_ptr(page_desc);
return mock_type(int);
}
int __wrap_trace_path(struct ftl_map *map, struct ftl_page_desc *new_page_desc,
uint32_t *page, uint32_t va)
{
struct ftl_page_desc *ret_page_desc;
uint32_t ret_page;
check_expected_ptr(map);
check_expected_ptr(new_page_desc);
check_expected_ptr(page);
check_expected(va);
ret_page_desc = mock_type(struct ftl_page_desc *);
if (new_page_desc)
memcpy(new_page_desc, ret_page_desc, sizeof *new_page_desc);
ret_page = mock_type(uint32_t);
if (page)
*page = ret_page;
return mock_type(int);
}

@ -0,0 +1,3 @@
#pragma once
int __wrap_prepare_head(struct ftl_map *map);

@ -0,0 +1,124 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
int next_upage(struct ftl_map *map, uint32_t p);
static void test_upage0(void **state)
{
struct ftl_map map;
uint32_t ret;
(void)state;
map.log2_pages_per_group = ilog2(16);
map.log2_groups_per_block = ilog2(1);
map.nblocks = 4;
ret = next_upage(&map, 0);
assert_int_equal(ret, 1);
}
static void test_upage14(void **state)
{
struct ftl_map map;
uint32_t ret;
(void)state;
map.log2_pages_per_group = ilog2(16);
map.log2_groups_per_block = ilog2(1);
map.nblocks = 4;
ret = next_upage(&map, 14);
assert_int_equal(ret, 16);
}
static void test_upage15(void **state)
{
struct ftl_map map;
uint32_t ret;
(void)state;
map.log2_pages_per_group = ilog2(16);
map.log2_groups_per_block = ilog2(1);
map.nblocks = 4;
ret = next_upage(&map, 15);
assert_int_equal(ret, 16);
}
static void test_upage16(void **state)
{
struct ftl_map map;
uint32_t ret;
(void)state;
map.log2_pages_per_group = ilog2(16);
map.log2_groups_per_block = ilog2(1);
map.nblocks = 4;
ret = next_upage(&map, 16);
assert_int_equal(ret, 17);
}
static void test_upage62(void **state)
{
struct ftl_map map;
uint32_t ret;
(void)state;
map.log2_pages_per_group = ilog2(16);
map.log2_groups_per_block = ilog2(1);
map.nblocks = 4;
ret = next_upage(&map, 62);
assert_int_equal(ret, 0);
}
static void test_upage63(void **state)
{
struct ftl_map map;
uint32_t ret;
(void)state;
map.log2_pages_per_group = ilog2(16);
map.log2_groups_per_block = ilog2(1);
map.nblocks = 4;
ret = next_upage(&map, 63);
assert_int_equal(ret, 0);
}
int test_next_upage(void)
{
const struct CMUnitTest tests[] = {
{ "next_upage: upage=0", test_upage0, NULL, NULL, NULL },
{ "next_upage: upage=14", test_upage14, NULL, NULL, NULL },
{ "next_upage: upage=15", test_upage15, NULL, NULL, NULL },
{ "next_upage: upage=16", test_upage16, NULL, NULL, NULL },
{ "next_upage: upage=62 (wraparound)", test_upage62, NULL, NULL, NULL },
{ "next_upage: upage=63 (wraparound)", test_upage63, NULL, NULL, NULL },
};
return cmocka_run_group_tests_name("next_upage", tests, NULL, NULL);
}

@ -0,0 +1,182 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
int __real_read_page_desc(struct ftl_map *map,
struct ftl_page_desc *page_desc, uint32_t upage);
static void test_upage0(void **state)
{
struct ftl_map map;
struct ftl_page_desc page_desc, ret_page_desc;
size_t ret;
(void)state;
memcpy(&page_desc.magic, "page", 4);
expect_value(__wrap_flash_read, addr, (16 - 1) * 4 * KIB +
sizeof(struct ftl_page_group));
expect_value(__wrap_flash_read, len, sizeof page_desc);
will_return(__wrap_flash_read, sizeof page_desc);
will_return(__wrap_flash_read, &page_desc);
map.log2_pages_per_group = ilog2(16);
map.log2_page_size = ilog2(4 * KIB);
ret = __real_read_page_desc(&map, &ret_page_desc, 0);
assert_int_equal(ret, 0);
}
static void test_upage14(void **state)
{
struct ftl_map map;
struct ftl_page_desc page_desc, ret_page_desc;
size_t ret;
(void)state;
memcpy(&page_desc.magic, "page", 4);
expect_value(__wrap_flash_read, addr, (16 - 1) * 4 * KIB +
sizeof(struct ftl_page_group) + 14 * sizeof(struct ftl_page_desc));
expect_value(__wrap_flash_read, len, sizeof page_desc);
will_return(__wrap_flash_read, sizeof page_desc);
will_return(__wrap_flash_read, &page_desc);
map.log2_pages_per_group = ilog2(16);
map.log2_page_size = ilog2(4 * KIB);
ret = __real_read_page_desc(&map, &ret_page_desc, 14);
assert_int_equal(ret, 0);
}
static void test_upage15(void **state)
{
struct ftl_map map;
struct ftl_page_desc ret_page_desc;
size_t ret;
(void)state;
map.log2_pages_per_group = ilog2(16);
map.log2_page_size = ilog2(4 * KIB);
ret = __real_read_page_desc(&map, &ret_page_desc, 15);
assert_int_equal(ret, -1);
}
static void test_upage16(void **state)
{
struct ftl_map map;
struct ftl_page_desc page_desc, ret_page_desc;
size_t ret;
(void)state;
memcpy(&page_desc.magic, "page", 4);
expect_value(__wrap_flash_read, addr, (32 - 1) * 4 * KIB +
sizeof(struct ftl_page_group));
expect_value(__wrap_flash_read, len, sizeof page_desc);
will_return(__wrap_flash_read, sizeof page_desc);
will_return(__wrap_flash_read, &page_desc);
map.log2_pages_per_group = ilog2(16);
map.log2_page_size = ilog2(4 * KIB);
ret = __real_read_page_desc(&map, &ret_page_desc, 16);
assert_int_equal(ret, 0);
}
static void test_upage30(void **state)
{
struct ftl_map map;
struct ftl_page_desc page_desc, ret_page_desc;
size_t ret;
(void)state;
memcpy(&page_desc.magic, "page", 4);
expect_value(__wrap_flash_read, addr, (32 - 1) * 4 * KIB +
sizeof(struct ftl_page_group) + 14 * sizeof(struct ftl_page_desc));
expect_value(__wrap_flash_read, len, sizeof page_desc);
will_return(__wrap_flash_read, sizeof page_desc);
will_return(__wrap_flash_read, &page_desc);
map.log2_pages_per_group = ilog2(16);
map.log2_page_size = ilog2(4 * KIB);
ret = __real_read_page_desc(&map, &ret_page_desc, 30);
assert_int_equal(ret, 0);
}
static void test_upage31(void **state)
{
struct ftl_map map;
struct ftl_page_desc ret_page_desc;
size_t ret;
(void)state;
map.log2_pages_per_group = ilog2(16);
map.log2_page_size = ilog2(4 * KIB);
ret = __real_read_page_desc(&map, &ret_page_desc, 31);
assert_int_equal(ret, -1);
}
static void test_upage32(void **state)
{
struct ftl_map map;
struct ftl_page_desc page_desc, ret_page_desc;
size_t ret;
(void)state;
memcpy(&page_desc.magic, "page", 4);
expect_value(__wrap_flash_read, addr, (48 - 1) * 4 * KIB +
sizeof(struct ftl_page_group));
expect_value(__wrap_flash_read, len, sizeof page_desc);
will_return(__wrap_flash_read, sizeof page_desc);
will_return(__wrap_flash_read, &page_desc);
map.log2_pages_per_group = ilog2(16);
map.log2_page_size = ilog2(4 * KIB);
ret = __real_read_page_desc(&map, &ret_page_desc, 32);
assert_int_equal(ret, 0);
}
int test_read_page_desc(void)
{
const struct CMUnitTest tests[] = {
{ "read_page_desc: upage=0", test_upage0, NULL, NULL, NULL },
{ "read_page_desc: upage=14", test_upage14, NULL, NULL, NULL },
{ "read_page_desc: upage=15 (invalid)", test_upage15, NULL, NULL, NULL },
{ "read_page_desc: upage=16", test_upage16, NULL, NULL, NULL },
{ "read_page_desc: upage=30", test_upage30, NULL, NULL, NULL },
{ "read_page_desc: upage=31 (invalid)", test_upage31, NULL, NULL, NULL },
{ "read_page_desc: upage=32", test_upage32, NULL, NULL, NULL },
};
return cmocka_run_group_tests_name("read_page_desc", tests, NULL, NULL);
}

@ -0,0 +1,95 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
int read_page_group(struct ftl_map *map,
struct ftl_page_group *group, uint32_t upage);
static void test_magic(void **state)
{
struct ftl_map map;
struct ftl_page_group group, ret_group;
size_t ret;
(void)state;
memcpy(&group.magic, "FtL", 3);
expect_value(__wrap_flash_read, addr, (16 - 1) * 4 * KIB);
expect_value(__wrap_flash_read, len, sizeof group);
will_return(__wrap_flash_read, sizeof group);
will_return(__wrap_flash_read, &group);
map.log2_pages_per_group = ilog2(16);
map.log2_page_size = ilog2(4 * KIB);
ret = read_page_group(&map, &ret_group, 0);
assert_int_equal(ret, -1);
}
static void test_group0(void **state)
{
struct ftl_map map;
struct ftl_page_group group, ret_group;
size_t ret;
(void)state;
memcpy(&group.magic, "FTL", 3);
expect_value(__wrap_flash_read, addr, (16 - 1) * 4 * KIB);
expect_value(__wrap_flash_read, len, sizeof group);
will_return(__wrap_flash_read, sizeof group);
will_return(__wrap_flash_read, &group);
map.log2_pages_per_group = ilog2(16);
map.log2_page_size = ilog2(4 * KIB);
ret = read_page_group(&map, &ret_group, 0);
assert_int_equal(ret, 0);
}
static void test_group1(void **state)
{
struct ftl_map map;
struct ftl_page_group group, ret_group;
size_t ret;
(void)state;
memcpy(&group.magic, "FTL", 3);
expect_value(__wrap_flash_read, addr, (2 * 16 - 1) * 4 * KIB);
expect_value(__wrap_flash_read, len, sizeof group);
will_return(__wrap_flash_read, sizeof group);
will_return(__wrap_flash_read, &group);
map.log2_pages_per_group = ilog2(16);
map.log2_page_size = ilog2(4 * KIB);
ret = read_page_group(&map, &ret_group, 1);
assert_int_equal(ret, 0);
}
int test_read_page_group(void)
{
const struct CMUnitTest tests[] = {
{ "read_page_group: invalid magic", test_magic, NULL, NULL, NULL },
{ "read_page_group: group=0", test_group0, NULL, NULL, NULL },
{ "read_page_group: group=1", test_group1, NULL, NULL, NULL },
};
return cmocka_run_group_tests_name("read_page_group", tests, NULL, NULL);
}

@ -0,0 +1,197 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
int __real_trace_path(struct ftl_map *map, struct ftl_page_desc *new_page_desc,
uint32_t *page, uint32_t va);
static void test_empty_root(void **state)
{
struct ftl_map map;
uint32_t page;
int ret;
(void)state;
map.log2_page_size = ilog2(4 * KIB);
map.root = UINT32_MAX;
ret = __real_trace_path(&map, NULL, &page, 0);
assert_int_equal(ret, -2);
}
static void test_exact_match(void **state)
{
struct ftl_page_desc descs[] = {
{
.va = 0xdeadbeef,
.subtrees = {
[0] = 19,
[1] = 21,
[2] = 38,
[3] = 81,
[4] = 24,
[5] = 64,
[6] = 25,
[7] = 15,
[8] = 0,
[9] = 73,
[10] = 49,
[11] = 22,
[12] = 85,
[13] = 45,
[14] = 98,
[15] = 7,
[16] = 72,
[17] = 26,
[18] = 92,
[19] = 16,
[20] = 12,
[21] = 57,
[22] = 52,
[23] = 39,
[24] = 28,
[25] = 42,
[26] = 37,
[27] = 87,
[28] = 5,
[29] = 27,
[30] = 30,
[31] = 47,
},
},
};
struct ftl_page_desc ret_page_desc;
struct ftl_map map;
uint32_t page;
int ret;
(void)state;
map.log2_page_size = ilog2(4 * KIB);
map.root = 75;
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 75);
will_return(__wrap_read_page_desc, descs);
will_return(__wrap_read_page_desc, 0);
ret = __real_trace_path(&map, &ret_page_desc, &page, 0xdeadbeef);
assert_int_equal(ret, 0);
assert_int_equal(page, 75);
assert_memory_equal(&ret_page_desc, descs, sizeof ret_page_desc);
}
static void test_walk(void **state)
{
struct ftl_page_desc descs[] = {
{
.va = 0x7,
.subtrees = {
[0] = UINT32_MAX,
[28] = 43,
[29] = UINT32_MAX,
},
},
{
.va = 0xb,
.subtrees = {
[0] = UINT32_MAX,
[28] = 42,
[29] = 44,
[30] = UINT32_MAX,
},
},
{
.va = 0xd,
.subtrees = {
[0] = UINT32_MAX,
[29] = 43,
[30] = 45,
[31] = UINT32_MAX,
},
},
{
.va = 0xe,
.subtrees = {
[0] = UINT32_MAX,
[30] = 44,
[31] = 46,
},
},
{
.va = 0xf,
.subtrees = {
UINT32_MAX,
},
},
};
struct ftl_page_desc ret_page_desc;
struct ftl_map map;
uint32_t page;
int ret;
(void)state;
map.log2_page_size = ilog2(4 * KIB);
map.root = 42;
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 42);
will_return(__wrap_read_page_desc, descs);
will_return(__wrap_read_page_desc, 0);
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 43);
will_return(__wrap_read_page_desc, descs + 1);
will_return(__wrap_read_page_desc, 0);
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 44);
will_return(__wrap_read_page_desc, descs + 2);
will_return(__wrap_read_page_desc, 0);
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 45);
will_return(__wrap_read_page_desc, descs + 3);
will_return(__wrap_read_page_desc, 0);
expect_value(__wrap_read_page_desc, map, &map);
expect_value(__wrap_read_page_desc, upage, 46);
will_return(__wrap_read_page_desc, descs + 3);
will_return(__wrap_read_page_desc, 0);
ret = __real_trace_path(&map, &ret_page_desc, &page, 0xf);
assert_int_equal(ret, 0);
assert_int_equal(page, 46);
assert_int_equal(ret_page_desc.va, 0xf);
assert_int_equal(ret_page_desc.subtrees[28], 42);
assert_int_equal(ret_page_desc.subtrees[29], 43);
assert_int_equal(ret_page_desc.subtrees[30], 44);
assert_int_equal(ret_page_desc.subtrees[31], 45);
}
int test_trace_path(void)
{
const struct CMUnitTest tests[] = {
{ "trace_path: empty root", test_empty_root, NULL, NULL, NULL },
{ "trace_path: exact match", test_exact_match, NULL, NULL, NULL },
{ "trace_path: page descriptor walk", test_walk, NULL, NULL, NULL },
};
return cmocka_run_group_tests_name("trace_path", tests, NULL, NULL);
}

@ -0,0 +1,258 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
int __real_write_page_desc(struct ftl_map *map,
struct ftl_page_desc *page_desc);
static int test_setup(void **state)
{
struct ftl_map *map;
if (!(map = test_malloc(sizeof(*map))))
return -1;
map->log2_pages_per_group = ilog2(16);
map->log2_groups_per_block = ilog2(1);
map->log2_page_size = ilog2(4 * KIB);
map->nblocks = 4;
map->epoch = 0;
*state = map;
return 0;
}
static int test_teardown(void **state)
{
struct ftl_map *map = *state;
test_free(map);
return 0;
}
static void test_head0(void **state)
{
struct ftl_map *map = *state;
struct ftl_page_desc page_desc, ret_page_desc;
size_t ret;
expect_value(__wrap_flash_is_erased, addr, 16 - 1);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 0);
expect_value(__wrap_flash_write, addr, (16 - 1) * 4 * KIB +
sizeof(struct ftl_page_group));
expect_value(__wrap_flash_write, len, sizeof page_desc);
will_return(__wrap_flash_write, sizeof page_desc);
will_return(__wrap_flash_write, &ret_page_desc);
map->head = 0;
ret = __real_write_page_desc(map, &page_desc);
assert_int_equal(ret, 0);
assert_int_equal(map->root, 0);
assert_int_equal(map->head, 1);
assert_int_equal(map->epoch, 0);
assert_memory_equal(ret_page_desc.magic, "page", 4);
}
static void test_head14(void **state)
{
struct ftl_map *map = *state;
struct ftl_page_desc page_desc, ret_page_desc;
size_t ret;
expect_value(__wrap_flash_is_erased, addr, 16 - 1);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 0);
expect_value(__wrap_flash_write, addr, (16 - 1) * 4 * KIB +
sizeof(struct ftl_page_group) + 14 * sizeof(struct ftl_page_desc));
expect_value(__wrap_flash_write, len, sizeof page_desc);
will_return(__wrap_flash_write, sizeof page_desc);
will_return(__wrap_flash_write, &ret_page_desc);
map->head = 14;
ret = __real_write_page_desc(map, &page_desc);
assert_int_equal(ret, 0);
assert_int_equal(map->root, 14);
assert_int_equal(map->head, 16);
assert_int_equal(map->epoch, 0);
assert_memory_equal(ret_page_desc.magic, "page", 4);
}
static void test_head16(void **state)
{
struct ftl_map *map = *state;
struct ftl_page_desc page_desc, ret_page_desc;
size_t ret;
expect_value(__wrap_flash_is_erased, addr, 2 * 16 - 1);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 0);
expect_value(__wrap_flash_write, addr, (2 * 16 - 1) * 4 * KIB +
sizeof(struct ftl_page_group));
expect_value(__wrap_flash_write, len, sizeof page_desc);
will_return(__wrap_flash_write, sizeof page_desc);
will_return(__wrap_flash_write, &ret_page_desc);
map->head = 16;
ret = __real_write_page_desc(map, &page_desc);
assert_int_equal(ret, 0);
assert_int_equal(map->root, 16);
assert_int_equal(map->head, 17);
assert_int_equal(map->epoch, 0);
assert_memory_equal(ret_page_desc.magic, "page", 4);
}
static void test_head30(void **state)
{
struct ftl_map *map = *state;
struct ftl_page_desc page_desc, ret_page_desc;
size_t ret;
expect_value(__wrap_flash_is_erased, addr, 2 * 16 - 1);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 0);
expect_value(__wrap_flash_write, addr, (2 * 16 - 1) * 4 * KIB +
sizeof(struct ftl_page_group) + 14 * sizeof(struct ftl_page_desc));
expect_value(__wrap_flash_write, len, sizeof page_desc);
will_return(__wrap_flash_write, sizeof page_desc);
will_return(__wrap_flash_write, &ret_page_desc);
map->head = 30;
ret = __real_write_page_desc(map, &page_desc);
assert_int_equal(ret, 0);
assert_int_equal(map->root, 30);
assert_int_equal(map->head, 32);
assert_int_equal(map->epoch, 0);
assert_memory_equal(ret_page_desc.magic, "page", 4);
}
static void test_head32(void **state)
{
struct ftl_map *map = *state;
struct ftl_page_desc page_desc, ret_page_desc;
size_t ret;
expect_value(__wrap_flash_is_erased, addr, 3 * 16 - 1);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 0);
expect_value(__wrap_flash_write, addr, (3 * 16 - 1) * 4 * KIB +
sizeof(struct ftl_page_group));
expect_value(__wrap_flash_write, len, sizeof page_desc);
will_return(__wrap_flash_write, sizeof page_desc);
will_return(__wrap_flash_write, &ret_page_desc);
map->head = 32;
ret = __real_write_page_desc(map, &page_desc);
assert_int_equal(ret, 0);
assert_int_equal(map->root, 32);
assert_int_equal(map->head, 33);
assert_int_equal(map->epoch, 0);
assert_memory_equal(ret_page_desc.magic, "page", 4);
}
static void test_epoch_wraparound(void **state)
{
struct ftl_map *map = *state;
struct ftl_page_desc page_desc, ret_page_desc;
size_t ret;
expect_value(__wrap_flash_is_erased, addr, 4 * 16 - 1);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 0);
expect_value(__wrap_flash_write, addr, (4 * 16 - 1) * 4 * KIB +
sizeof(struct ftl_page_group) + 14 * sizeof(struct ftl_page_desc));
expect_value(__wrap_flash_write, len, sizeof page_desc);
will_return(__wrap_flash_write, sizeof page_desc);
will_return(__wrap_flash_write, &ret_page_desc);
map->head = 62;
ret = __real_write_page_desc(map, &page_desc);
assert_int_equal(ret, 0);
assert_int_equal(map->root, 62);
assert_int_equal(map->head, 0);
assert_int_equal(map->epoch, 1);
assert_memory_equal(ret_page_desc.magic, "page", 4);
}
static void test_group_header(void **state)
{
struct ftl_map *map = *state;
struct ftl_page_group ret_group;
struct ftl_page_desc page_desc, ret_page_desc;
size_t ret;
expect_value(__wrap_flash_is_erased, addr, 16 - 1);
expect_value(__wrap_flash_is_erased, len, 1);
will_return(__wrap_flash_is_erased, 1);
expect_value(__wrap_flash_write, addr, (16 - 1) * 4 * KIB);
expect_value(__wrap_flash_write, len, sizeof ret_group);
will_return(__wrap_flash_write, sizeof ret_group);
will_return(__wrap_flash_write, &ret_group);
expect_value(__wrap_flash_write, addr, (16 - 1) * 4 * KIB +
sizeof(struct ftl_page_group));
expect_value(__wrap_flash_write, len, sizeof page_desc);
will_return(__wrap_flash_write, sizeof page_desc);
will_return(__wrap_flash_write, &ret_page_desc);
map->head = 0;
map->epoch = 0;
ret = __real_write_page_desc(map, &page_desc);
assert_int_equal(ret, 0);
assert_int_equal(map->root, 0);
assert_int_equal(map->head, 1);
assert_int_equal(map->epoch, 0);
assert_memory_equal(ret_group.magic, "FTL", 3);
assert_int_equal(ret_group.epoch, map->epoch);
assert_memory_equal(ret_page_desc.magic, "page", 4);
}
int test_write_page_desc(void)
{
const struct CMUnitTest tests[] = {
{ "write_page_desc: head=0", test_head0, NULL, NULL, NULL },
{ "write_page_desc: head=14", test_head14, NULL, NULL, NULL },
{ "write_page_desc: head=16", test_head16, NULL, NULL, NULL },
{ "write_page_desc: head=30", test_head30, NULL, NULL, NULL },
{ "write_page_desc: head=32", test_head32, NULL, NULL, NULL },
{ "write_page_desc: epoch wraparound", test_epoch_wraparound, NULL, NULL, NULL },
{ "write_page_desc: group header", test_group_header, NULL, NULL, NULL },
};
return cmocka_run_group_tests_name("write_page_desc", tests, test_setup,
test_teardown);
}

@ -0,0 +1,131 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
int write_upage(struct ftl_map *map, const void *page,
struct ftl_page_desc *page_desc);
static void test_prepare_head(void **state)
{
struct ftl_map map;
struct ftl_page_desc page_desc;
int ret;
(void)state;
map.log2_page_size = ilog2(4 * KIB);
map.head = 0;
will_return(__wrap_prepare_head, -1);
ret = write_upage(&map, NULL, &page_desc);
assert_int_equal(ret, -1);
}
static void test_null_page(void **state)
{
struct ftl_map map;
struct ftl_page_desc page_desc;
int ret;
(void)state;
map.log2_page_size = ilog2(4 * KIB);
map.head = 0;
will_return(__wrap_prepare_head, 0);
expect_value(__wrap_write_page_desc, map, &map);
expect_value(__wrap_write_page_desc, page_desc, &page_desc);
will_return(__wrap_write_page_desc, 0);
ret = write_upage(&map, NULL, &page_desc);
assert_int_equal(ret, 0);
}
static void test_page0(void **state)
{
struct ftl_map map;
char page[4 * KIB], ret_page[4 * KIB];
struct ftl_page_desc page_desc;
int ret;
(void)state;
memset(page, 0x5a, sizeof page);
memset(ret_page, 0, sizeof ret_page);
map.log2_page_size = ilog2(4 * KIB);
map.head = 0;
will_return(__wrap_prepare_head, 0);
expect_value(__wrap_flash_write, addr, 0);
expect_value(__wrap_flash_write, len, 4 * KIB);
will_return(__wrap_flash_write, 4 * KIB);
will_return(__wrap_flash_write, &ret_page);
expect_value(__wrap_write_page_desc, map, &map);
expect_value(__wrap_write_page_desc, page_desc, &page_desc);
will_return(__wrap_write_page_desc, 0);
ret = write_upage(&map, &page, &page_desc);
assert_int_equal(ret, 0);
assert_memory_equal(page, ret_page, sizeof page);
}
static void test_page42(void **state)
{
struct ftl_map map;
char page[4 * KIB], ret_page[4 * KIB];
struct ftl_page_desc page_desc;
int ret;
(void)state;
memset(page, 0x5a, sizeof page);
memset(ret_page, 0, sizeof ret_page);
map.log2_page_size = ilog2(4 * KIB);
map.head = 42;
will_return(__wrap_prepare_head, 0);
expect_value(__wrap_flash_write, addr, 42 * 4 * KIB);
expect_value(__wrap_flash_write, len, 4 * KIB);
will_return(__wrap_flash_write, 4 * KIB);
will_return(__wrap_flash_write, &ret_page);
expect_value(__wrap_write_page_desc, map, &map);
expect_value(__wrap_write_page_desc, page_desc, &page_desc);
will_return(__wrap_write_page_desc, 0);
ret = write_upage(&map, &page, &page_desc);
assert_int_equal(ret, 0);
assert_memory_equal(page, ret_page, sizeof page);
}
int test_write_upage(void)
{
const struct CMUnitTest tests[] = {
{ "write_upage: prepare_head() = -1", test_prepare_head, NULL, NULL, NULL },
{ "write_upage: null page", test_null_page, NULL, NULL, NULL },
{ "write_upage: head=0", test_page0, NULL, NULL, NULL },
{ "write_upage: head=42", test_page42, NULL, NULL, NULL },
};
return cmocka_run_group_tests_name("write_upage", tests, NULL, NULL);
}

@ -0,0 +1,23 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <bitops.h>
#include <flash.h>
#include <ftl.h>
#include <macros.h>
int test_ftl(void);
int main(void)
{
int count = 0;
count += test_ftl();
return count;
}
Loading…
Cancel
Save