diff --git a/Makefile b/Makefile index 6a6ec64..17d3ab3 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ CC ?= gcc AR ?= ar SIZE ?= size -SRC += $(wildcard *.c emubd/*.c) +SRC += $(wildcard *.c rambd/*.c filebd/*.c) OBJ := $(SRC:.c=.o) DEP := $(SRC:.c=.d) ASM := $(SRC:.c=.s) diff --git a/emubd/lfs_emubd.c b/emubd/lfs_emubd.c deleted file mode 100644 index 9a29ff1..0000000 --- a/emubd/lfs_emubd.c +++ /dev/null @@ -1,414 +0,0 @@ -/* - * Block device emulated on standard files - * - * Copyright (c) 2017, Arm Limited. All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - */ -#include "emubd/lfs_emubd.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -// Emulated block device utils -static inline void lfs_emubd_tole32(lfs_emubd_t *emu) { - emu->cfg.read_size = lfs_tole32(emu->cfg.read_size); - emu->cfg.prog_size = lfs_tole32(emu->cfg.prog_size); - emu->cfg.block_size = lfs_tole32(emu->cfg.block_size); - emu->cfg.block_count = lfs_tole32(emu->cfg.block_count); - - emu->stats.read_count = lfs_tole32(emu->stats.read_count); - emu->stats.prog_count = lfs_tole32(emu->stats.prog_count); - emu->stats.erase_count = lfs_tole32(emu->stats.erase_count); - - for (unsigned i = 0; i < sizeof(emu->history.blocks) / - sizeof(emu->history.blocks[0]); i++) { - emu->history.blocks[i] = lfs_tole32(emu->history.blocks[i]); - } -} - -static inline void lfs_emubd_fromle32(lfs_emubd_t *emu) { - emu->cfg.read_size = lfs_fromle32(emu->cfg.read_size); - emu->cfg.prog_size = lfs_fromle32(emu->cfg.prog_size); - emu->cfg.block_size = lfs_fromle32(emu->cfg.block_size); - emu->cfg.block_count = lfs_fromle32(emu->cfg.block_count); - - emu->stats.read_count = lfs_fromle32(emu->stats.read_count); - emu->stats.prog_count = lfs_fromle32(emu->stats.prog_count); - emu->stats.erase_count = lfs_fromle32(emu->stats.erase_count); - - for (unsigned i = 0; i < sizeof(emu->history.blocks) / - sizeof(emu->history.blocks[0]); i++) { - emu->history.blocks[i] = lfs_fromle32(emu->history.blocks[i]); - } -} - - -// Block device emulated on existing filesystem -int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { - LFS_TRACE("lfs_emubd_create(%p {.context=%p, " - ".read=%p, .prog=%p, .erase=%p, .sync=%p, " - ".read_size=%"PRIu32", .prog_size=%"PRIu32", " - ".block_size=%"PRIu32", .block_count=%"PRIu32"}, \"%s\")", - (void*)cfg, cfg->context, - (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, - (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, - cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, - path); - lfs_emubd_t *emu = cfg->context; - emu->cfg.read_size = cfg->read_size; - emu->cfg.prog_size = cfg->prog_size; - emu->cfg.block_size = cfg->block_size; - emu->cfg.block_count = cfg->block_count; - - // Allocate buffer for creating children files - size_t pathlen = strlen(path); - emu->path = malloc(pathlen + 1 + LFS_NAME_MAX + 1); - if (!emu->path) { - int err = -ENOMEM; - LFS_TRACE("lfs_emubd_create -> %"PRId32, err); - return err; - } - - strcpy(emu->path, path); - emu->path[pathlen] = '/'; - emu->child = &emu->path[pathlen+1]; - memset(emu->child, '\0', LFS_NAME_MAX+1); - - // Create directory if it doesn't exist - int err = mkdir(path, 0777); - if (err && errno != EEXIST) { - err = -errno; - LFS_TRACE("lfs_emubd_create -> %"PRId32, err); - return err; - } - - // Load stats to continue incrementing - snprintf(emu->child, LFS_NAME_MAX, ".stats"); - FILE *f = fopen(emu->path, "r"); - if (!f) { - memset(&emu->stats, LFS_EMUBD_ERASE_VALUE, sizeof(emu->stats)); - } else { - size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f); - lfs_emubd_fromle32(emu); - if (res < 1) { - err = -errno; - LFS_TRACE("lfs_emubd_create -> %"PRId32, err); - fclose(f); - return err; - } - - err = fclose(f); - if (err) { - err = -errno; - LFS_TRACE("lfs_emubd_create -> %"PRId32, err); - return err; - } - } - - // Load history - snprintf(emu->child, LFS_NAME_MAX, ".history"); - f = fopen(emu->path, "r"); - if (!f) { - memset(&emu->history, 0, sizeof(emu->history)); - } else { - size_t res = fread(&emu->history, sizeof(emu->history), 1, f); - lfs_emubd_fromle32(emu); - if (res < 1) { - err = -errno; - LFS_TRACE("lfs_emubd_create -> %"PRId32, err); - fclose(f); - return err; - } - - err = fclose(f); - if (err) { - err = -errno; - LFS_TRACE("lfs_emubd_create -> %"PRId32, err); - return err; - } - } - - LFS_TRACE("lfs_emubd_create -> %"PRId32, 0); - return 0; -} - -void lfs_emubd_destroy(const struct lfs_config *cfg) { - LFS_TRACE("lfs_emubd_destroy(%p)", (void*)cfg); - lfs_emubd_sync(cfg); - - lfs_emubd_t *emu = cfg->context; - free(emu->path); - LFS_TRACE("lfs_emubd_destroy -> %s", "void"); -} - -int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block, - lfs_off_t off, void *buffer, lfs_size_t size) { - LFS_TRACE("lfs_emubd_read(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", - (void*)cfg, block, off, buffer, size); - lfs_emubd_t *emu = cfg->context; - uint8_t *data = buffer; - - // Check if read is valid - LFS_ASSERT(off % cfg->read_size == 0); - LFS_ASSERT(size % cfg->read_size == 0); - LFS_ASSERT(block < cfg->block_count); - - // Zero out buffer for debugging - memset(data, 0, size); - - // Read data - snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block); - - FILE *f = fopen(emu->path, "rb"); - if (!f && errno != ENOENT) { - int err = -errno; - LFS_TRACE("lfs_emubd_read -> %d", err); - return err; - } - - if (f) { - int err = fseek(f, off, SEEK_SET); - if (err) { - err = -errno; - LFS_TRACE("lfs_emubd_read -> %d", err); - fclose(f); - return err; - } - - size_t res = fread(data, 1, size, f); - if (res < size && !feof(f)) { - err = -errno; - LFS_TRACE("lfs_emubd_read -> %d", err); - fclose(f); - return err; - } - - err = fclose(f); - if (err) { - err = -errno; - LFS_TRACE("lfs_emubd_read -> %d", err); - return err; - } - } - - emu->stats.read_count += size; - LFS_TRACE("lfs_emubd_read -> %d", 0); - return 0; -} - -int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, - lfs_off_t off, const void *buffer, lfs_size_t size) { - LFS_TRACE("lfs_emubd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", - (void*)cfg, block, off, buffer, size); - lfs_emubd_t *emu = cfg->context; - const uint8_t *data = buffer; - - // Check if write is valid - LFS_ASSERT(off % cfg->prog_size == 0); - LFS_ASSERT(size % cfg->prog_size == 0); - LFS_ASSERT(block < cfg->block_count); - - // Program data - snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block); - - FILE *f = fopen(emu->path, "r+b"); - if (!f) { - int err = (errno == EACCES) ? 0 : -errno; - LFS_TRACE("lfs_emubd_prog -> %d", err); - return err; - } - - // Check that file was erased - LFS_ASSERT(f); - - int err = fseek(f, off, SEEK_SET); - if (err) { - err = -errno; - LFS_TRACE("lfs_emubd_prog -> %d", err); - fclose(f); - return err; - } - - size_t res = fwrite(data, 1, size, f); - if (res < size) { - err = -errno; - LFS_TRACE("lfs_emubd_prog -> %d", err); - fclose(f); - return err; - } - - err = fseek(f, off, SEEK_SET); - if (err) { - err = -errno; - LFS_TRACE("lfs_emubd_prog -> %d", err); - fclose(f); - return err; - } - - uint8_t dat; - res = fread(&dat, 1, 1, f); - if (res < 1) { - err = -errno; - LFS_TRACE("lfs_emubd_prog -> %d", err); - fclose(f); - return err; - } - - err = fclose(f); - if (err) { - err = -errno; - LFS_TRACE("lfs_emubd_prog -> %d", err); - return err; - } - - // update history and stats - if (block != emu->history.blocks[0]) { - memmove(&emu->history.blocks[1], &emu->history.blocks[0], - sizeof(emu->history) - sizeof(emu->history.blocks[0])); - emu->history.blocks[0] = block; - } - - emu->stats.prog_count += size; - LFS_TRACE("lfs_emubd_prog -> %d", 0); - return 0; -} - -int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { - LFS_TRACE("lfs_emubd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); - lfs_emubd_t *emu = cfg->context; - - // Check if erase is valid - LFS_ASSERT(block < cfg->block_count); - - // Erase the block - snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block); - struct stat st; - int err = stat(emu->path, &st); - if (err && errno != ENOENT) { - err = -errno; - LFS_TRACE("lfs_emubd_erase -> %d", err); - return err; - } - - if (!err && S_ISREG(st.st_mode) && (S_IWUSR & st.st_mode)) { - err = unlink(emu->path); - if (err) { - err = -errno; - LFS_TRACE("lfs_emubd_erase -> %d", err); - return err; - } - } - - if (err || (S_ISREG(st.st_mode) && (S_IWUSR & st.st_mode))) { - FILE *f = fopen(emu->path, "w"); - if (!f) { - err = -errno; - LFS_TRACE("lfs_emubd_erase -> %d", err); - return err; - } - - err = fclose(f); - if (err) { - err = -errno; - LFS_TRACE("lfs_emubd_erase -> %d", err); - return err; - } - } - - emu->stats.erase_count += cfg->block_size; - LFS_TRACE("lfs_emubd_erase -> %d", 0); - return 0; -} - -int lfs_emubd_sync(const struct lfs_config *cfg) { - LFS_TRACE("lfs_emubd_sync(%p)", (void*)cfg); - lfs_emubd_t *emu = cfg->context; - - // Just write out info/stats for later lookup - snprintf(emu->child, LFS_NAME_MAX, ".config"); - FILE *f = fopen(emu->path, "w"); - if (!f) { - int err = -errno; - LFS_TRACE("lfs_emubd_sync -> %d", err); - return err; - } - - lfs_emubd_tole32(emu); - size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f); - lfs_emubd_fromle32(emu); - if (res < 1) { - int err = -errno; - LFS_TRACE("lfs_emubd_sync -> %d", err); - fclose(f); - return err; - } - - int err = fclose(f); - if (err) { - err = -errno; - LFS_TRACE("lfs_emubd_sync -> %d", err); - return err; - } - - snprintf(emu->child, LFS_NAME_MAX, ".stats"); - f = fopen(emu->path, "w"); - if (!f) { - err = -errno; - LFS_TRACE("lfs_emubd_sync -> %d", err); - return err; - } - - lfs_emubd_tole32(emu); - res = fwrite(&emu->stats, sizeof(emu->stats), 1, f); - lfs_emubd_fromle32(emu); - if (res < 1) { - err = -errno; - LFS_TRACE("lfs_emubd_sync -> %d", err); - fclose(f); - return err; - } - - err = fclose(f); - if (err) { - err = -errno; - LFS_TRACE("lfs_emubd_sync -> %d", err); - return err; - } - - snprintf(emu->child, LFS_NAME_MAX, ".history"); - f = fopen(emu->path, "w"); - if (!f) { - err = -errno; - LFS_TRACE("lfs_emubd_sync -> %d", err); - return err; - } - - lfs_emubd_tole32(emu); - res = fwrite(&emu->history, sizeof(emu->history), 1, f); - lfs_emubd_fromle32(emu); - if (res < 1) { - err = -errno; - LFS_TRACE("lfs_emubd_sync -> %d", err); - fclose(f); - return err; - } - - err = fclose(f); - if (err) { - err = -errno; - LFS_TRACE("lfs_emubd_sync -> %d", err); - return err; - } - - LFS_TRACE("lfs_emubd_sync -> %d", 0); - return 0; -} diff --git a/emubd/lfs_emubd.h b/emubd/lfs_emubd.h deleted file mode 100644 index 0fd78c1..0000000 --- a/emubd/lfs_emubd.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Block device emulated on standard files - * - * Copyright (c) 2017, Arm Limited. All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - */ -#ifndef LFS_EMUBD_H -#define LFS_EMUBD_H - -#include "lfs.h" -#include "lfs_util.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - -// Config options -#ifndef LFS_EMUBD_ERASE_VALUE -#define LFS_EMUBD_ERASE_VALUE 0x00 -#endif - - -// The emu bd state -typedef struct lfs_emubd { - char *path; - char *child; - - struct { - uint64_t read_count; - uint64_t prog_count; - uint64_t erase_count; - } stats; - - struct { - lfs_block_t blocks[4]; - } history; - - struct { - uint32_t read_size; - uint32_t prog_size; - uint32_t block_size; - uint32_t block_count; - } cfg; -} lfs_emubd_t; - - -// Create a block device using path for the directory to store blocks -int lfs_emubd_create(const struct lfs_config *cfg, const char *path); - -// Clean up memory associated with emu block device -void lfs_emubd_destroy(const struct lfs_config *cfg); - -// Read a block -int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block, - lfs_off_t off, void *buffer, lfs_size_t size); - -// Program a block -// -// The block must have previously been erased. -int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, - lfs_off_t off, const void *buffer, lfs_size_t size); - -// Erase a block -// -// A block must be erased before being programmed. The -// state of an erased block is undefined. -int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block); - -// Sync the block device -int lfs_emubd_sync(const struct lfs_config *cfg); - - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/filebd/lfs_filebd.c b/filebd/lfs_filebd.c new file mode 100644 index 0000000..d7fd89f --- /dev/null +++ b/filebd/lfs_filebd.c @@ -0,0 +1,198 @@ +/* + * Block device emulated in a file + * + * Copyright (c) 2017, Arm Limited. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "filebd/lfs_filebd.h" + +#include +#include +#include + +int lfs_filebd_createcfg(const struct lfs_config *cfg, const char *path, + const struct lfs_filebd_config *filecfg) { + LFS_TRACE("lfs_filebd_createcfg(%p {.context=%p, " + ".read=%p, .prog=%p, .erase=%p, .sync=%p, " + ".read_size=%"PRIu32", .prog_size=%"PRIu32", " + ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " + "\"%s\", " + "%p {.erase_value=%"PRId32"})", + (void*)cfg, cfg->context, + (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, + (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, + cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, + path, (void*)filecfg, filecfg->erase_value); + lfs_filebd_t *bd = cfg->context; + bd->cfg = filecfg; + + // open file + bd->fd = open(path, O_RDWR | O_CREAT, 0666); + if (bd->fd < 0) { + int err = -errno; + LFS_TRACE("lfs_filebd_createcfg -> %d", err); + return err; + } + + LFS_TRACE("lfs_filebd_createcfg -> %d", 0); + return 0; +} + +int lfs_filebd_create(const struct lfs_config *cfg, const char *path) { + LFS_TRACE("lfs_filebd_create(%p {.context=%p, " + ".read=%p, .prog=%p, .erase=%p, .sync=%p, " + ".read_size=%"PRIu32", .prog_size=%"PRIu32", " + ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " + "\"%s\")", + (void*)cfg, cfg->context, + (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, + (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, + cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, + path); + static const struct lfs_filebd_config defaults = {.erase_value=-1}; + int err = lfs_filebd_createcfg(cfg, path, &defaults); + LFS_TRACE("lfs_filebd_create -> %d", err); + return err; +} + +void lfs_filebd_destroy(const struct lfs_config *cfg) { + LFS_TRACE("lfs_filebd_destroy(%p)", (void*)cfg); + lfs_filebd_t *bd = cfg->context; + close(bd->fd); + LFS_TRACE("lfs_filebd_destroy -> %s", "void"); +} + +int lfs_filebd_read(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, void *buffer, lfs_size_t size) { + LFS_TRACE("lfs_filebd_read(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", + (void*)cfg, block, off, buffer, size); + lfs_filebd_t *bd = cfg->context; + + // check if read is valid + LFS_ASSERT(off % cfg->read_size == 0); + LFS_ASSERT(size % cfg->read_size == 0); + LFS_ASSERT(block < cfg->block_count); + + // zero for reproducability (in case file is truncated) + if (bd->cfg->erase_value != -1) { + memset(buffer, bd->cfg->erase_value, size); + } + + // read + off_t res1 = lseek(bd->fd, + (off_t)block*cfg->block_size + (off_t)off, SEEK_SET); + if (res1 < 0) { + int err = -errno; + LFS_TRACE("lfs_filebd_read -> %d", err); + return err; + } + + ssize_t res2 = read(bd->fd, buffer, size); + if (res2 < 0) { + int err = -errno; + LFS_TRACE("lfs_filebd_read -> %d", err); + return err; + } + + LFS_TRACE("lfs_filebd_read -> %d", 0); + return 0; +} + +int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, const void *buffer, lfs_size_t size) { + LFS_TRACE("lfs_filebd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", + (void*)cfg, block, off, buffer, size); + lfs_filebd_t *bd = cfg->context; + + // check if write is valid + LFS_ASSERT(off % cfg->prog_size == 0); + LFS_ASSERT(size % cfg->prog_size == 0); + LFS_ASSERT(block < cfg->block_count); + + // check that data was erased? only needed for testing + if (bd->cfg->erase_value != -1) { + off_t res1 = lseek(bd->fd, + (off_t)block*cfg->block_size + (off_t)off, SEEK_SET); + if (res1 < 0) { + int err = -errno; + LFS_TRACE("lfs_filebd_prog -> %d", err); + return err; + } + + for (lfs_off_t i = 0; i < size; i++) { + uint8_t c; + ssize_t res2 = read(bd->fd, &c, 1); + if (res2 < 0) { + int err = -errno; + LFS_TRACE("lfs_filebd_prog -> %d", err); + return err; + } + + LFS_ASSERT(c == bd->cfg->erase_value); + } + } + + // program data + off_t res1 = lseek(bd->fd, + (off_t)block*cfg->block_size + (off_t)off, SEEK_SET); + if (res1 < 0) { + int err = -errno; + LFS_TRACE("lfs_filebd_prog -> %d", err); + return err; + } + + ssize_t res2 = write(bd->fd, buffer, size); + if (res2 < 0) { + int err = -errno; + LFS_TRACE("lfs_filebd_prog -> %d", err); + return err; + } + + LFS_TRACE("lfs_filebd_prog -> %d", 0); + return 0; +} + +int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) { + LFS_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); + lfs_filebd_t *bd = cfg->context; + + // check if erase is valid + LFS_ASSERT(block < cfg->block_count); + + // erase, only needed for testing + if (bd->cfg->erase_value != -1) { + off_t res1 = lseek(bd->fd, (off_t)block*cfg->block_size, SEEK_SET); + if (res1 < 0) { + int err = -errno; + LFS_TRACE("lfs_filebd_erase -> %d", err); + return err; + } + + for (lfs_off_t i = 0; i < cfg->block_size; i++) { + ssize_t res2 = write(bd->fd, &(uint8_t){bd->cfg->erase_value}, 1); + if (res2 < 0) { + int err = -errno; + LFS_TRACE("lfs_filebd_erase -> %d", err); + return err; + } + } + } + + LFS_TRACE("lfs_filebd_erase -> %d", 0); + return 0; +} + +int lfs_filebd_sync(const struct lfs_config *cfg) { + LFS_TRACE("lfs_filebd_sync(%p)", (void*)cfg); + // file sync + lfs_filebd_t *bd = cfg->context; + int err = fsync(bd->fd); + if (err) { + err = -errno; + LFS_TRACE("lfs_filebd_sync -> %d", 0); + return err; + } + + LFS_TRACE("lfs_filebd_sync -> %d", 0); + return 0; +} diff --git a/filebd/lfs_filebd.h b/filebd/lfs_filebd.h new file mode 100644 index 0000000..ae28858 --- /dev/null +++ b/filebd/lfs_filebd.h @@ -0,0 +1,64 @@ +/* + * Block device emulated in a file + * + * Copyright (c) 2017, Arm Limited. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef LFS_FILEBD_H +#define LFS_FILEBD_H + +#include "lfs.h" +#include "lfs_util.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +// filebd config (optional) +struct lfs_filebd_config { + // 8-bit erase value to simulate erasing with. -1 indicates no erase + // occurs, which is still a valid block device + int32_t erase_value; +}; + +// filebd state +typedef struct lfs_filebd { + int fd; + const struct lfs_filebd_config *cfg; +} lfs_filebd_t; + + +// Create a file block device using the geometry in lfs_config +int lfs_filebd_create(const struct lfs_config *cfg, const char *path); +int lfs_filebd_createcfg(const struct lfs_config *cfg, const char *path, + const struct lfs_filebd_config *ramcfg); + +// Clean up memory associated with block device +void lfs_filebd_destroy(const struct lfs_config *cfg); + +// Read a block +int lfs_filebd_read(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, void *buffer, lfs_size_t size); + +// Program a block +// +// The block must have previously been erased. +int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, const void *buffer, lfs_size_t size); + +// Erase a block +// +// A block must be erased before being programmed. The +// state of an erased block is undefined. +int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block); + +// Sync the block device +int lfs_filebd_sync(const struct lfs_config *cfg); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/rambd/lfs_rambd.c b/rambd/lfs_rambd.c new file mode 100644 index 0000000..5bf8cc1 --- /dev/null +++ b/rambd/lfs_rambd.c @@ -0,0 +1,137 @@ +/* + * Block device emulated in RAM + * + * Copyright (c) 2017, Arm Limited. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "rambd/lfs_rambd.h" + +int lfs_rambd_createcfg(const struct lfs_config *cfg, + const struct lfs_rambd_config *ramcfg) { + LFS_TRACE("lfs_rambd_createcfg(%p {.context=%p, " + ".read=%p, .prog=%p, .erase=%p, .sync=%p, " + ".read_size=%"PRIu32", .prog_size=%"PRIu32", " + ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " + "%p {.erase_value=%"PRId32", .buffer=%p})", + (void*)cfg, cfg->context, + (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, + (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, + cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, + (void*)ramcfg, ramcfg->erase_value, ramcfg->buffer); + lfs_rambd_t *bd = cfg->context; + bd->cfg = ramcfg; + + // allocate buffer? + if (bd->cfg->buffer) { + bd->buffer = bd->cfg->buffer; + } else { + bd->buffer = lfs_malloc(cfg->block_size * cfg->block_count); + if (!bd->buffer) { + LFS_TRACE("lfs_rambd_createcfg -> %d", LFS_ERR_NOMEM); + return LFS_ERR_NOMEM; + } + } + + // zero for reproducability? + if (bd->cfg->erase_value != -1) { + memset(bd->buffer, bd->cfg->erase_value, + cfg->block_size * cfg->block_count); + } + + LFS_TRACE("lfs_rambd_createcfg -> %d", 0); + return 0; +} + +int lfs_rambd_create(const struct lfs_config *cfg) { + LFS_TRACE("lfs_rambd_create(%p {.context=%p, " + ".read=%p, .prog=%p, .erase=%p, .sync=%p, " + ".read_size=%"PRIu32", .prog_size=%"PRIu32", " + ".block_size=%"PRIu32", .block_count=%"PRIu32"})", + (void*)cfg, cfg->context, + (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, + (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, + cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count); + static const struct lfs_rambd_config defaults = {.erase_value=-1}; + int err = lfs_rambd_createcfg(cfg, &defaults); + LFS_TRACE("lfs_rambd_create -> %d", err); + return err; +} + +void lfs_rambd_destroy(const struct lfs_config *cfg) { + LFS_TRACE("lfs_rambd_destroy(%p)", (void*)cfg); + // clean up memory + lfs_rambd_t *bd = cfg->context; + if (!bd->cfg->buffer) { + lfs_free(bd->buffer); + } + LFS_TRACE("lfs_rambd_destroy -> %s", "void"); +} + +int lfs_rambd_read(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, void *buffer, lfs_size_t size) { + LFS_TRACE("lfs_rambd_read(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", + (void*)cfg, block, off, buffer, size); + lfs_rambd_t *bd = cfg->context; + + // check if read is valid + LFS_ASSERT(off % cfg->read_size == 0); + LFS_ASSERT(size % cfg->read_size == 0); + LFS_ASSERT(block < cfg->block_count); + + // read data + memcpy(buffer, &bd->buffer[block*cfg->block_size + off], size); + + LFS_TRACE("lfs_rambd_read -> %d", 0); + return 0; +} + +int lfs_rambd_prog(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, const void *buffer, lfs_size_t size) { + LFS_TRACE("lfs_rambd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", + (void*)cfg, block, off, buffer, size); + lfs_rambd_t *bd = cfg->context; + + // check if write is valid + LFS_ASSERT(off % cfg->prog_size == 0); + LFS_ASSERT(size % cfg->prog_size == 0); + LFS_ASSERT(block < cfg->block_count); + + // check that data was erased? only needed for testing + if (bd->cfg->erase_value != -1) { + for (lfs_off_t i = 0; i < size; i++) { + LFS_ASSERT(bd->buffer[block*cfg->block_size + off + i] == + bd->cfg->erase_value); + } + } + + // program data + memcpy(&bd->buffer[block*cfg->block_size + off], buffer, size); + + LFS_TRACE("lfs_rambd_prog -> %d", 0); + return 0; +} + +int lfs_rambd_erase(const struct lfs_config *cfg, lfs_block_t block) { + LFS_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); + lfs_rambd_t *bd = cfg->context; + + // check if erase is valid + LFS_ASSERT(block < cfg->block_count); + + // erase, only needed for testing + if (bd->cfg->erase_value != -1) { + memset(&bd->buffer[block*cfg->block_size], + bd->cfg->erase_value, cfg->block_size); + } + + LFS_TRACE("lfs_rambd_erase -> %d", 0); + return 0; +} + +int lfs_rambd_sync(const struct lfs_config *cfg) { + LFS_TRACE("lfs_rambd_sync(%p)", (void*)cfg); + // sync does nothing because we aren't backed by anything real + (void)cfg; + LFS_TRACE("lfs_rambd_sync -> %d", 0); + return 0; +} diff --git a/rambd/lfs_rambd.h b/rambd/lfs_rambd.h new file mode 100644 index 0000000..a4b9515 --- /dev/null +++ b/rambd/lfs_rambd.h @@ -0,0 +1,67 @@ +/* + * Block device emulated in RAM + * + * Copyright (c) 2017, Arm Limited. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef LFS_RAMBD_H +#define LFS_RAMBD_H + +#include "lfs.h" +#include "lfs_util.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +// rambd config (optional) +struct lfs_rambd_config { + // 8-bit erase value to simulate erasing with. -1 indicates no erase + // occurs, which is still a valid block device + int32_t erase_value; + + // Optional statically allocated buffer for the block device. + void *buffer; +}; + +// rambd state +typedef struct lfs_rambd { + uint8_t *buffer; + const struct lfs_rambd_config *cfg; +} lfs_rambd_t; + + +// Create a RAM block device using the geometry in lfs_config +int lfs_rambd_create(const struct lfs_config *cfg); +int lfs_rambd_createcfg(const struct lfs_config *cfg, + const struct lfs_rambd_config *ramcfg); + +// Clean up memory associated with block device +void lfs_rambd_destroy(const struct lfs_config *cfg); + +// Read a block +int lfs_rambd_read(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, void *buffer, lfs_size_t size); + +// Program a block +// +// The block must have previously been erased. +int lfs_rambd_prog(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, const void *buffer, lfs_size_t size); + +// Erase a block +// +// A block must be erased before being programmed. The +// state of an erased block is undefined. +int lfs_rambd_erase(const struct lfs_config *cfg, lfs_block_t block); + +// Sync the block device +int lfs_rambd_sync(const struct lfs_config *cfg); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/scripts/test_.py b/scripts/test_.py index 5a481e4..27002d9 100755 --- a/scripts/test_.py +++ b/scripts/test_.py @@ -15,7 +15,6 @@ import subprocess as sp import base64 import sys import copy -import shutil import shlex TESTDIR = 'tests_' @@ -40,8 +39,10 @@ $(foreach target,$(SRC),$(eval $(FLATTEN))) GLOBALS = """ //////////////// AUTOGENERATED TEST //////////////// #include "lfs.h" -#include "emubd/lfs_emubd.h" +#include "filebd/lfs_filebd.h" +#include "rambd/lfs_rambd.h" #include +const char *LFS_DISK = NULL; """ DEFINES = { "LFS_READ_SIZE": 16, @@ -51,23 +52,25 @@ DEFINES = { "LFS_BLOCK_CYCLES": 1024, "LFS_CACHE_SIZE": "(64 % LFS_PROG_SIZE == 0 ? 64 : LFS_PROG_SIZE)", "LFS_LOOKAHEAD_SIZE": 16, + "LFS_ERASE_VALUE": 0xff, } PROLOGUE = """ // prologue __attribute__((unused)) lfs_t lfs; - __attribute__((unused)) lfs_emubd_t bd; + __attribute__((unused)) lfs_filebd_t filebd; + __attribute__((unused)) lfs_rambd_t rambd; __attribute__((unused)) lfs_file_t file; __attribute__((unused)) lfs_dir_t dir; __attribute__((unused)) struct lfs_info info; __attribute__((unused)) uint8_t buffer[1024]; __attribute__((unused)) char path[1024]; - + __attribute__((unused)) const struct lfs_config cfg = { - .context = &bd, - .read = &lfs_emubd_read, - .prog = &lfs_emubd_prog, - .erase = &lfs_emubd_erase, - .sync = &lfs_emubd_sync, + .context = LFS_DISK ? (void*)&filebd : (void*)&rambd, + .read = LFS_DISK ? &lfs_filebd_read : &lfs_rambd_read, + .prog = LFS_DISK ? &lfs_filebd_prog : &lfs_rambd_prog, + .erase = LFS_DISK ? &lfs_filebd_erase : &lfs_rambd_erase, + .sync = LFS_DISK ? &lfs_filebd_sync : &lfs_rambd_sync, .read_size = LFS_READ_SIZE, .prog_size = LFS_PROG_SIZE, @@ -78,11 +81,26 @@ PROLOGUE = """ .lookahead_size = LFS_LOOKAHEAD_SIZE, }; - lfs_emubd_create(&cfg, "blocks"); + __attribute__((unused)) const struct lfs_filebd_config filecfg = { + .erase_value = LFS_ERASE_VALUE, + }; + __attribute__((unused)) const struct lfs_rambd_config ramcfg = { + .erase_value = LFS_ERASE_VALUE, + }; + + if (LFS_DISK) { + lfs_filebd_createcfg(&cfg, LFS_DISK, &filecfg); + } else { + lfs_rambd_createcfg(&cfg, &ramcfg); + } """ EPILOGUE = """ // epilogue - lfs_emubd_destroy(&cfg); + if (LFS_DISK) { + lfs_filebd_destroy(&cfg); + } else { + lfs_rambd_destroy(&cfg); + } """ PASS = '\033[32m✓\033[0m' FAIL = '\033[31m✗\033[0m' @@ -133,7 +151,8 @@ class TestCase: f.write(') {\n') for k, v in sorted(self.defines.items()): - f.write(4*' '+'#define %s %s\n' % (k, v)) + if k not in self.suite.defines: + f.write(4*' '+'#define %s %s\n' % (k, v)) f.write(PROLOGUE) f.write('\n') @@ -148,27 +167,34 @@ class TestCase: f.write('\n') for k, v in sorted(self.defines.items()): - f.write(4*' '+'#undef %s\n' % k) + if k not in self.suite.defines: + f.write(4*' '+'#undef %s\n' % k) f.write('}\n') def test(self, exec=[], persist=False, gdb=False, failure=None, **args): - # clear disk first - if not persist: - shutil.rmtree('blocks', True) - # build command cmd = exec + ['./%s.test' % self.suite.path, repr(self.caseno), repr(self.permno)] + if persist: + cmd.append(self.suite.path + '.test.disk') # failed? drop into debugger? if gdb and failure: - cmd = (['gdb', '-ex', 'r' - ] + (['-ex', 'up'] if failure.assert_ else []) + [ - '--args'] + cmd) + ncmd = ['gdb'] + if gdb == 'assert': + ncmd.extend(['-ex', 'r']) + if failure.assert_: + ncmd.extend(['-ex', 'up']) + elif gdb == 'start': + ncmd.extend([ + '-ex', 'b %s:%d' % (self.suite.path, self.lineno), + '-ex', 'r']) + ncmd.extend(['--args'] + cmd) + if args.get('verbose', False): - print(' '.join(shlex.quote(c) for c in cmd)) - sys.exit(sp.call(cmd)) + print(' '.join(shlex.quote(c) for c in ncmd)) + sys.exit(sp.call(ncmd)) # run test case! stdout = [] @@ -231,22 +257,27 @@ class ReentrantTestCase(TestCase): if not self.reentrant: return - for cycles in it.count(1): - npersist = persist or cycles > 1 + # clear disk first? + if not persist: + try: + os.remove(self.suite.path + '.test.disk') + except FileNotFoundError: + pass + for cycles in it.count(1): # exact cycle we should drop into debugger? if gdb and failure and failure.cycleno == cycles: - return super().test(exec=exec, persist=npersist, + return super().test(exec=exec, persist=True, gdb=gdb, failure=failure, **args) - # run tests, but kill the program after lfs_emubd_prog/erase has + # run tests, but kill the program after prog/erase has # been hit n cycles. We exit with a special return code if the # program has not finished, since this isn't a test failure. nexec = exec + [ 'gdb', '-batch-silent', '-ex', 'handle all nostop', - '-ex', 'b lfs_emubd_prog', - '-ex', 'b lfs_emubd_erase', + '-ex', 'b lfs_filebd_prog', + '-ex', 'b lfs_filebd_erase', '-ex', 'r', ] + cycles*['-ex', 'c'] + [ '-ex', 'q ' @@ -255,7 +286,7 @@ class ReentrantTestCase(TestCase): '33', '--args'] try: - return super().test(exec=nexec, persist=npersist, **args) + return super().test(exec=nexec, persist=True, **args) except TestFailure as nfailure: if nfailure.returncode == 33: continue @@ -370,10 +401,11 @@ class TestSuite: f.write('\n') f.write('int main(int argc, char **argv) {\n') - f.write(4*' '+'int case_ = (argc == 3) ? atoi(argv[1]) : 0;\n') - f.write(4*' '+'int perm = (argc == 3) ? atoi(argv[2]) : 0;\n') + f.write(4*' '+'int case_ = (argc >= 3) ? atoi(argv[1]) : 0;\n') + f.write(4*' '+'int perm = (argc >= 3) ? atoi(argv[2]) : 0;\n') + f.write(4*' '+'LFS_DISK = (argc >= 4) ? argv[3] : NULL;\n') for perm in self.perms: - f.write(4*' '+'if (argc != 3 || ' + f.write(4*' '+'if (argc < 3 || ' '(case_ == %d && perm == %d)) { ' % ( perm.caseno, perm.permno)) f.write('test_case%d(' % perm.caseno) @@ -590,8 +622,9 @@ if __name__ == "__main__": help="Run all tests instead of stopping on first error. Useful for CI.") parser.add_argument('-p', '--persist', action='store_true', help="Don't reset the tests disk before each test.") - parser.add_argument('-g', '--gdb', action='store_true', - help="Drop into gdb on failure.") + parser.add_argument('-g', '--gdb', choices=['init', 'start', 'assert'], + nargs='?', const='assert', + help="Drop into gdb on test failure.") parser.add_argument('--valgrind', action='store_true', help="Run non-leaky tests under valgrind to check for memory leaks.") parser.add_argument('--reentrant', action='store_true',