119ea8026Sopenharmony_ci/* 219ea8026Sopenharmony_ci * Testing block device, wraps filebd and rambd while providing a bunch 319ea8026Sopenharmony_ci * of hooks for testing littlefs in various conditions. 419ea8026Sopenharmony_ci * 519ea8026Sopenharmony_ci * Copyright (c) 2022, The littlefs authors. 619ea8026Sopenharmony_ci * Copyright (c) 2017, Arm Limited. All rights reserved. 719ea8026Sopenharmony_ci * SPDX-License-Identifier: BSD-3-Clause 819ea8026Sopenharmony_ci */ 919ea8026Sopenharmony_ci#include "bd/lfs_testbd.h" 1019ea8026Sopenharmony_ci 1119ea8026Sopenharmony_ci#include <stdlib.h> 1219ea8026Sopenharmony_ci 1319ea8026Sopenharmony_ci 1419ea8026Sopenharmony_ciint lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path, 1519ea8026Sopenharmony_ci const struct lfs_testbd_config *bdcfg) { 1619ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_createcfg(%p {.context=%p, " 1719ea8026Sopenharmony_ci ".read=%p, .prog=%p, .erase=%p, .sync=%p, " 1819ea8026Sopenharmony_ci ".read_size=%"PRIu32", .prog_size=%"PRIu32", " 1919ea8026Sopenharmony_ci ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " 2019ea8026Sopenharmony_ci "\"%s\", " 2119ea8026Sopenharmony_ci "%p {.erase_value=%"PRId32", .erase_cycles=%"PRIu32", " 2219ea8026Sopenharmony_ci ".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", " 2319ea8026Sopenharmony_ci ".buffer=%p, .wear_buffer=%p})", 2419ea8026Sopenharmony_ci (void*)cfg, cfg->context, 2519ea8026Sopenharmony_ci (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, 2619ea8026Sopenharmony_ci (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, 2719ea8026Sopenharmony_ci cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, 2819ea8026Sopenharmony_ci path, (void*)bdcfg, bdcfg->erase_value, bdcfg->erase_cycles, 2919ea8026Sopenharmony_ci bdcfg->badblock_behavior, bdcfg->power_cycles, 3019ea8026Sopenharmony_ci bdcfg->buffer, bdcfg->wear_buffer); 3119ea8026Sopenharmony_ci lfs_testbd_t *bd = cfg->context; 3219ea8026Sopenharmony_ci bd->cfg = bdcfg; 3319ea8026Sopenharmony_ci 3419ea8026Sopenharmony_ci // setup testing things 3519ea8026Sopenharmony_ci bd->persist = path; 3619ea8026Sopenharmony_ci bd->power_cycles = bd->cfg->power_cycles; 3719ea8026Sopenharmony_ci 3819ea8026Sopenharmony_ci if (bd->cfg->erase_cycles) { 3919ea8026Sopenharmony_ci if (bd->cfg->wear_buffer) { 4019ea8026Sopenharmony_ci bd->wear = bd->cfg->wear_buffer; 4119ea8026Sopenharmony_ci } else { 4219ea8026Sopenharmony_ci bd->wear = lfs_malloc(sizeof(lfs_testbd_wear_t)*cfg->block_count); 4319ea8026Sopenharmony_ci if (!bd->wear) { 4419ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM); 4519ea8026Sopenharmony_ci return LFS_ERR_NOMEM; 4619ea8026Sopenharmony_ci } 4719ea8026Sopenharmony_ci } 4819ea8026Sopenharmony_ci 4919ea8026Sopenharmony_ci memset(bd->wear, 0, sizeof(lfs_testbd_wear_t) * cfg->block_count); 5019ea8026Sopenharmony_ci } 5119ea8026Sopenharmony_ci 5219ea8026Sopenharmony_ci // create underlying block device 5319ea8026Sopenharmony_ci if (bd->persist) { 5419ea8026Sopenharmony_ci bd->u.file.cfg = (struct lfs_filebd_config){ 5519ea8026Sopenharmony_ci .erase_value = bd->cfg->erase_value, 5619ea8026Sopenharmony_ci }; 5719ea8026Sopenharmony_ci int err = lfs_filebd_createcfg(cfg, path, &bd->u.file.cfg); 5819ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err); 5919ea8026Sopenharmony_ci return err; 6019ea8026Sopenharmony_ci } else { 6119ea8026Sopenharmony_ci bd->u.ram.cfg = (struct lfs_rambd_config){ 6219ea8026Sopenharmony_ci .erase_value = bd->cfg->erase_value, 6319ea8026Sopenharmony_ci .buffer = bd->cfg->buffer, 6419ea8026Sopenharmony_ci }; 6519ea8026Sopenharmony_ci int err = lfs_rambd_createcfg(cfg, &bd->u.ram.cfg); 6619ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err); 6719ea8026Sopenharmony_ci return err; 6819ea8026Sopenharmony_ci } 6919ea8026Sopenharmony_ci} 7019ea8026Sopenharmony_ci 7119ea8026Sopenharmony_ciint lfs_testbd_create(const struct lfs_config *cfg, const char *path) { 7219ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_create(%p {.context=%p, " 7319ea8026Sopenharmony_ci ".read=%p, .prog=%p, .erase=%p, .sync=%p, " 7419ea8026Sopenharmony_ci ".read_size=%"PRIu32", .prog_size=%"PRIu32", " 7519ea8026Sopenharmony_ci ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " 7619ea8026Sopenharmony_ci "\"%s\")", 7719ea8026Sopenharmony_ci (void*)cfg, cfg->context, 7819ea8026Sopenharmony_ci (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, 7919ea8026Sopenharmony_ci (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, 8019ea8026Sopenharmony_ci cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, 8119ea8026Sopenharmony_ci path); 8219ea8026Sopenharmony_ci static const struct lfs_testbd_config defaults = {.erase_value=-1}; 8319ea8026Sopenharmony_ci int err = lfs_testbd_createcfg(cfg, path, &defaults); 8419ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_create -> %d", err); 8519ea8026Sopenharmony_ci return err; 8619ea8026Sopenharmony_ci} 8719ea8026Sopenharmony_ci 8819ea8026Sopenharmony_ciint lfs_testbd_destroy(const struct lfs_config *cfg) { 8919ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_destroy(%p)", (void*)cfg); 9019ea8026Sopenharmony_ci lfs_testbd_t *bd = cfg->context; 9119ea8026Sopenharmony_ci if (bd->cfg->erase_cycles && !bd->cfg->wear_buffer) { 9219ea8026Sopenharmony_ci lfs_free(bd->wear); 9319ea8026Sopenharmony_ci } 9419ea8026Sopenharmony_ci 9519ea8026Sopenharmony_ci if (bd->persist) { 9619ea8026Sopenharmony_ci int err = lfs_filebd_destroy(cfg); 9719ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err); 9819ea8026Sopenharmony_ci return err; 9919ea8026Sopenharmony_ci } else { 10019ea8026Sopenharmony_ci int err = lfs_rambd_destroy(cfg); 10119ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err); 10219ea8026Sopenharmony_ci return err; 10319ea8026Sopenharmony_ci } 10419ea8026Sopenharmony_ci} 10519ea8026Sopenharmony_ci 10619ea8026Sopenharmony_ci/// Internal mapping to block devices /// 10719ea8026Sopenharmony_cistatic int lfs_testbd_rawread(const struct lfs_config *cfg, lfs_block_t block, 10819ea8026Sopenharmony_ci lfs_off_t off, void *buffer, lfs_size_t size) { 10919ea8026Sopenharmony_ci lfs_testbd_t *bd = cfg->context; 11019ea8026Sopenharmony_ci if (bd->persist) { 11119ea8026Sopenharmony_ci return lfs_filebd_read(cfg, block, off, buffer, size); 11219ea8026Sopenharmony_ci } else { 11319ea8026Sopenharmony_ci return lfs_rambd_read(cfg, block, off, buffer, size); 11419ea8026Sopenharmony_ci } 11519ea8026Sopenharmony_ci} 11619ea8026Sopenharmony_ci 11719ea8026Sopenharmony_cistatic int lfs_testbd_rawprog(const struct lfs_config *cfg, lfs_block_t block, 11819ea8026Sopenharmony_ci lfs_off_t off, const void *buffer, lfs_size_t size) { 11919ea8026Sopenharmony_ci lfs_testbd_t *bd = cfg->context; 12019ea8026Sopenharmony_ci if (bd->persist) { 12119ea8026Sopenharmony_ci return lfs_filebd_prog(cfg, block, off, buffer, size); 12219ea8026Sopenharmony_ci } else { 12319ea8026Sopenharmony_ci return lfs_rambd_prog(cfg, block, off, buffer, size); 12419ea8026Sopenharmony_ci } 12519ea8026Sopenharmony_ci} 12619ea8026Sopenharmony_ci 12719ea8026Sopenharmony_cistatic int lfs_testbd_rawerase(const struct lfs_config *cfg, 12819ea8026Sopenharmony_ci lfs_block_t block) { 12919ea8026Sopenharmony_ci lfs_testbd_t *bd = cfg->context; 13019ea8026Sopenharmony_ci if (bd->persist) { 13119ea8026Sopenharmony_ci return lfs_filebd_erase(cfg, block); 13219ea8026Sopenharmony_ci } else { 13319ea8026Sopenharmony_ci return lfs_rambd_erase(cfg, block); 13419ea8026Sopenharmony_ci } 13519ea8026Sopenharmony_ci} 13619ea8026Sopenharmony_ci 13719ea8026Sopenharmony_cistatic int lfs_testbd_rawsync(const struct lfs_config *cfg) { 13819ea8026Sopenharmony_ci lfs_testbd_t *bd = cfg->context; 13919ea8026Sopenharmony_ci if (bd->persist) { 14019ea8026Sopenharmony_ci return lfs_filebd_sync(cfg); 14119ea8026Sopenharmony_ci } else { 14219ea8026Sopenharmony_ci return lfs_rambd_sync(cfg); 14319ea8026Sopenharmony_ci } 14419ea8026Sopenharmony_ci} 14519ea8026Sopenharmony_ci 14619ea8026Sopenharmony_ci/// block device API /// 14719ea8026Sopenharmony_ciint lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block, 14819ea8026Sopenharmony_ci lfs_off_t off, void *buffer, lfs_size_t size) { 14919ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_read(%p, " 15019ea8026Sopenharmony_ci "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", 15119ea8026Sopenharmony_ci (void*)cfg, block, off, buffer, size); 15219ea8026Sopenharmony_ci lfs_testbd_t *bd = cfg->context; 15319ea8026Sopenharmony_ci 15419ea8026Sopenharmony_ci // check if read is valid 15519ea8026Sopenharmony_ci LFS_ASSERT(off % cfg->read_size == 0); 15619ea8026Sopenharmony_ci LFS_ASSERT(size % cfg->read_size == 0); 15719ea8026Sopenharmony_ci LFS_ASSERT(block < cfg->block_count); 15819ea8026Sopenharmony_ci 15919ea8026Sopenharmony_ci // block bad? 16019ea8026Sopenharmony_ci if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles && 16119ea8026Sopenharmony_ci bd->cfg->badblock_behavior == LFS_TESTBD_BADBLOCK_READERROR) { 16219ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT); 16319ea8026Sopenharmony_ci return LFS_ERR_CORRUPT; 16419ea8026Sopenharmony_ci } 16519ea8026Sopenharmony_ci 16619ea8026Sopenharmony_ci // read 16719ea8026Sopenharmony_ci int err = lfs_testbd_rawread(cfg, block, off, buffer, size); 16819ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_read -> %d", err); 16919ea8026Sopenharmony_ci return err; 17019ea8026Sopenharmony_ci} 17119ea8026Sopenharmony_ci 17219ea8026Sopenharmony_ciint lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block, 17319ea8026Sopenharmony_ci lfs_off_t off, const void *buffer, lfs_size_t size) { 17419ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_prog(%p, " 17519ea8026Sopenharmony_ci "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", 17619ea8026Sopenharmony_ci (void*)cfg, block, off, buffer, size); 17719ea8026Sopenharmony_ci lfs_testbd_t *bd = cfg->context; 17819ea8026Sopenharmony_ci 17919ea8026Sopenharmony_ci // check if write is valid 18019ea8026Sopenharmony_ci LFS_ASSERT(off % cfg->prog_size == 0); 18119ea8026Sopenharmony_ci LFS_ASSERT(size % cfg->prog_size == 0); 18219ea8026Sopenharmony_ci LFS_ASSERT(block < cfg->block_count); 18319ea8026Sopenharmony_ci 18419ea8026Sopenharmony_ci // block bad? 18519ea8026Sopenharmony_ci if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles) { 18619ea8026Sopenharmony_ci if (bd->cfg->badblock_behavior == 18719ea8026Sopenharmony_ci LFS_TESTBD_BADBLOCK_PROGERROR) { 18819ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT); 18919ea8026Sopenharmony_ci return LFS_ERR_CORRUPT; 19019ea8026Sopenharmony_ci } else if (bd->cfg->badblock_behavior == 19119ea8026Sopenharmony_ci LFS_TESTBD_BADBLOCK_PROGNOOP || 19219ea8026Sopenharmony_ci bd->cfg->badblock_behavior == 19319ea8026Sopenharmony_ci LFS_TESTBD_BADBLOCK_ERASENOOP) { 19419ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); 19519ea8026Sopenharmony_ci return 0; 19619ea8026Sopenharmony_ci } 19719ea8026Sopenharmony_ci } 19819ea8026Sopenharmony_ci 19919ea8026Sopenharmony_ci // prog 20019ea8026Sopenharmony_ci int err = lfs_testbd_rawprog(cfg, block, off, buffer, size); 20119ea8026Sopenharmony_ci if (err) { 20219ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err); 20319ea8026Sopenharmony_ci return err; 20419ea8026Sopenharmony_ci } 20519ea8026Sopenharmony_ci 20619ea8026Sopenharmony_ci // lose power? 20719ea8026Sopenharmony_ci if (bd->power_cycles > 0) { 20819ea8026Sopenharmony_ci bd->power_cycles -= 1; 20919ea8026Sopenharmony_ci if (bd->power_cycles == 0) { 21019ea8026Sopenharmony_ci // sync to make sure we persist the last changes 21119ea8026Sopenharmony_ci LFS_ASSERT(lfs_testbd_rawsync(cfg) == 0); 21219ea8026Sopenharmony_ci // simulate power loss 21319ea8026Sopenharmony_ci exit(33); 21419ea8026Sopenharmony_ci } 21519ea8026Sopenharmony_ci } 21619ea8026Sopenharmony_ci 21719ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); 21819ea8026Sopenharmony_ci return 0; 21919ea8026Sopenharmony_ci} 22019ea8026Sopenharmony_ci 22119ea8026Sopenharmony_ciint lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) { 22219ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); 22319ea8026Sopenharmony_ci lfs_testbd_t *bd = cfg->context; 22419ea8026Sopenharmony_ci 22519ea8026Sopenharmony_ci // check if erase is valid 22619ea8026Sopenharmony_ci LFS_ASSERT(block < cfg->block_count); 22719ea8026Sopenharmony_ci 22819ea8026Sopenharmony_ci // block bad? 22919ea8026Sopenharmony_ci if (bd->cfg->erase_cycles) { 23019ea8026Sopenharmony_ci if (bd->wear[block] >= bd->cfg->erase_cycles) { 23119ea8026Sopenharmony_ci if (bd->cfg->badblock_behavior == 23219ea8026Sopenharmony_ci LFS_TESTBD_BADBLOCK_ERASEERROR) { 23319ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT); 23419ea8026Sopenharmony_ci return LFS_ERR_CORRUPT; 23519ea8026Sopenharmony_ci } else if (bd->cfg->badblock_behavior == 23619ea8026Sopenharmony_ci LFS_TESTBD_BADBLOCK_ERASENOOP) { 23719ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", 0); 23819ea8026Sopenharmony_ci return 0; 23919ea8026Sopenharmony_ci } 24019ea8026Sopenharmony_ci } else { 24119ea8026Sopenharmony_ci // mark wear 24219ea8026Sopenharmony_ci bd->wear[block] += 1; 24319ea8026Sopenharmony_ci } 24419ea8026Sopenharmony_ci } 24519ea8026Sopenharmony_ci 24619ea8026Sopenharmony_ci // erase 24719ea8026Sopenharmony_ci int err = lfs_testbd_rawerase(cfg, block); 24819ea8026Sopenharmony_ci if (err) { 24919ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err); 25019ea8026Sopenharmony_ci return err; 25119ea8026Sopenharmony_ci } 25219ea8026Sopenharmony_ci 25319ea8026Sopenharmony_ci // lose power? 25419ea8026Sopenharmony_ci if (bd->power_cycles > 0) { 25519ea8026Sopenharmony_ci bd->power_cycles -= 1; 25619ea8026Sopenharmony_ci if (bd->power_cycles == 0) { 25719ea8026Sopenharmony_ci // sync to make sure we persist the last changes 25819ea8026Sopenharmony_ci LFS_ASSERT(lfs_testbd_rawsync(cfg) == 0); 25919ea8026Sopenharmony_ci // simulate power loss 26019ea8026Sopenharmony_ci exit(33); 26119ea8026Sopenharmony_ci } 26219ea8026Sopenharmony_ci } 26319ea8026Sopenharmony_ci 26419ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); 26519ea8026Sopenharmony_ci return 0; 26619ea8026Sopenharmony_ci} 26719ea8026Sopenharmony_ci 26819ea8026Sopenharmony_ciint lfs_testbd_sync(const struct lfs_config *cfg) { 26919ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_sync(%p)", (void*)cfg); 27019ea8026Sopenharmony_ci int err = lfs_testbd_rawsync(cfg); 27119ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_sync -> %d", err); 27219ea8026Sopenharmony_ci return err; 27319ea8026Sopenharmony_ci} 27419ea8026Sopenharmony_ci 27519ea8026Sopenharmony_ci 27619ea8026Sopenharmony_ci/// simulated wear operations /// 27719ea8026Sopenharmony_cilfs_testbd_swear_t lfs_testbd_getwear(const struct lfs_config *cfg, 27819ea8026Sopenharmony_ci lfs_block_t block) { 27919ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)cfg, block); 28019ea8026Sopenharmony_ci lfs_testbd_t *bd = cfg->context; 28119ea8026Sopenharmony_ci 28219ea8026Sopenharmony_ci // check if block is valid 28319ea8026Sopenharmony_ci LFS_ASSERT(bd->cfg->erase_cycles); 28419ea8026Sopenharmony_ci LFS_ASSERT(block < cfg->block_count); 28519ea8026Sopenharmony_ci 28619ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_getwear -> %"PRIu32, bd->wear[block]); 28719ea8026Sopenharmony_ci return bd->wear[block]; 28819ea8026Sopenharmony_ci} 28919ea8026Sopenharmony_ci 29019ea8026Sopenharmony_ciint lfs_testbd_setwear(const struct lfs_config *cfg, 29119ea8026Sopenharmony_ci lfs_block_t block, lfs_testbd_wear_t wear) { 29219ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)cfg, block); 29319ea8026Sopenharmony_ci lfs_testbd_t *bd = cfg->context; 29419ea8026Sopenharmony_ci 29519ea8026Sopenharmony_ci // check if block is valid 29619ea8026Sopenharmony_ci LFS_ASSERT(bd->cfg->erase_cycles); 29719ea8026Sopenharmony_ci LFS_ASSERT(block < cfg->block_count); 29819ea8026Sopenharmony_ci 29919ea8026Sopenharmony_ci bd->wear[block] = wear; 30019ea8026Sopenharmony_ci 30119ea8026Sopenharmony_ci LFS_TESTBD_TRACE("lfs_testbd_setwear -> %d", 0); 30219ea8026Sopenharmony_ci return 0; 30319ea8026Sopenharmony_ci} 304