119ea8026Sopenharmony_ci/* 219ea8026Sopenharmony_ci * Emulating 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 1019ea8026Sopenharmony_ci#ifndef _POSIX_C_SOURCE 1119ea8026Sopenharmony_ci#define _POSIX_C_SOURCE 199309L 1219ea8026Sopenharmony_ci#endif 1319ea8026Sopenharmony_ci 1419ea8026Sopenharmony_ci#include "bd/lfs_emubd.h" 1519ea8026Sopenharmony_ci 1619ea8026Sopenharmony_ci#include <stdlib.h> 1719ea8026Sopenharmony_ci#include <fcntl.h> 1819ea8026Sopenharmony_ci#include <unistd.h> 1919ea8026Sopenharmony_ci#include <errno.h> 2019ea8026Sopenharmony_ci#include <time.h> 2119ea8026Sopenharmony_ci 2219ea8026Sopenharmony_ci#ifdef _WIN32 2319ea8026Sopenharmony_ci#include <windows.h> 2419ea8026Sopenharmony_ci#endif 2519ea8026Sopenharmony_ci 2619ea8026Sopenharmony_ci 2719ea8026Sopenharmony_ci// access to lazily-allocated/copy-on-write blocks 2819ea8026Sopenharmony_ci// 2919ea8026Sopenharmony_ci// Note we can only modify a block if we have exclusive access to it (rc == 1) 3019ea8026Sopenharmony_ci// 3119ea8026Sopenharmony_ci 3219ea8026Sopenharmony_cistatic lfs_emubd_block_t *lfs_emubd_incblock(lfs_emubd_block_t *block) { 3319ea8026Sopenharmony_ci if (block) { 3419ea8026Sopenharmony_ci block->rc += 1; 3519ea8026Sopenharmony_ci } 3619ea8026Sopenharmony_ci return block; 3719ea8026Sopenharmony_ci} 3819ea8026Sopenharmony_ci 3919ea8026Sopenharmony_cistatic void lfs_emubd_decblock(lfs_emubd_block_t *block) { 4019ea8026Sopenharmony_ci if (block) { 4119ea8026Sopenharmony_ci block->rc -= 1; 4219ea8026Sopenharmony_ci if (block->rc == 0) { 4319ea8026Sopenharmony_ci free(block); 4419ea8026Sopenharmony_ci } 4519ea8026Sopenharmony_ci } 4619ea8026Sopenharmony_ci} 4719ea8026Sopenharmony_ci 4819ea8026Sopenharmony_cistatic lfs_emubd_block_t *lfs_emubd_mutblock( 4919ea8026Sopenharmony_ci const struct lfs_config *cfg, 5019ea8026Sopenharmony_ci lfs_emubd_block_t **block) { 5119ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 5219ea8026Sopenharmony_ci lfs_emubd_block_t *block_ = *block; 5319ea8026Sopenharmony_ci if (block_ && block_->rc == 1) { 5419ea8026Sopenharmony_ci // rc == 1? can modify 5519ea8026Sopenharmony_ci return block_; 5619ea8026Sopenharmony_ci 5719ea8026Sopenharmony_ci } else if (block_) { 5819ea8026Sopenharmony_ci // rc > 1? need to create a copy 5919ea8026Sopenharmony_ci lfs_emubd_block_t *nblock = malloc( 6019ea8026Sopenharmony_ci sizeof(lfs_emubd_block_t) + bd->cfg->erase_size); 6119ea8026Sopenharmony_ci if (!nblock) { 6219ea8026Sopenharmony_ci return NULL; 6319ea8026Sopenharmony_ci } 6419ea8026Sopenharmony_ci 6519ea8026Sopenharmony_ci memcpy(nblock, block_, 6619ea8026Sopenharmony_ci sizeof(lfs_emubd_block_t) + bd->cfg->erase_size); 6719ea8026Sopenharmony_ci nblock->rc = 1; 6819ea8026Sopenharmony_ci 6919ea8026Sopenharmony_ci lfs_emubd_decblock(block_); 7019ea8026Sopenharmony_ci *block = nblock; 7119ea8026Sopenharmony_ci return nblock; 7219ea8026Sopenharmony_ci 7319ea8026Sopenharmony_ci } else { 7419ea8026Sopenharmony_ci // no block? need to allocate 7519ea8026Sopenharmony_ci lfs_emubd_block_t *nblock = malloc( 7619ea8026Sopenharmony_ci sizeof(lfs_emubd_block_t) + bd->cfg->erase_size); 7719ea8026Sopenharmony_ci if (!nblock) { 7819ea8026Sopenharmony_ci return NULL; 7919ea8026Sopenharmony_ci } 8019ea8026Sopenharmony_ci 8119ea8026Sopenharmony_ci nblock->rc = 1; 8219ea8026Sopenharmony_ci nblock->wear = 0; 8319ea8026Sopenharmony_ci 8419ea8026Sopenharmony_ci // zero for consistency 8519ea8026Sopenharmony_ci memset(nblock->data, 8619ea8026Sopenharmony_ci (bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0, 8719ea8026Sopenharmony_ci bd->cfg->erase_size); 8819ea8026Sopenharmony_ci 8919ea8026Sopenharmony_ci *block = nblock; 9019ea8026Sopenharmony_ci return nblock; 9119ea8026Sopenharmony_ci } 9219ea8026Sopenharmony_ci} 9319ea8026Sopenharmony_ci 9419ea8026Sopenharmony_ci 9519ea8026Sopenharmony_ci// emubd create/destroy 9619ea8026Sopenharmony_ci 9719ea8026Sopenharmony_ciint lfs_emubd_create(const struct lfs_config *cfg, 9819ea8026Sopenharmony_ci const struct lfs_emubd_config *bdcfg) { 9919ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_create(%p {.context=%p, " 10019ea8026Sopenharmony_ci ".read=%p, .prog=%p, .erase=%p, .sync=%p}, " 10119ea8026Sopenharmony_ci "%p {.read_size=%"PRIu32", .prog_size=%"PRIu32", " 10219ea8026Sopenharmony_ci ".erase_size=%"PRIu32", .erase_count=%"PRIu32", " 10319ea8026Sopenharmony_ci ".erase_value=%"PRId32", .erase_cycles=%"PRIu32", " 10419ea8026Sopenharmony_ci ".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", " 10519ea8026Sopenharmony_ci ".powerloss_behavior=%"PRIu8", .powerloss_cb=%p, " 10619ea8026Sopenharmony_ci ".powerloss_data=%p, .track_branches=%d})", 10719ea8026Sopenharmony_ci (void*)cfg, cfg->context, 10819ea8026Sopenharmony_ci (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, 10919ea8026Sopenharmony_ci (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, 11019ea8026Sopenharmony_ci (void*)bdcfg, 11119ea8026Sopenharmony_ci bdcfg->read_size, bdcfg->prog_size, bdcfg->erase_size, 11219ea8026Sopenharmony_ci bdcfg->erase_count, bdcfg->erase_value, bdcfg->erase_cycles, 11319ea8026Sopenharmony_ci bdcfg->badblock_behavior, bdcfg->power_cycles, 11419ea8026Sopenharmony_ci bdcfg->powerloss_behavior, (void*)(uintptr_t)bdcfg->powerloss_cb, 11519ea8026Sopenharmony_ci bdcfg->powerloss_data, bdcfg->track_branches); 11619ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 11719ea8026Sopenharmony_ci bd->cfg = bdcfg; 11819ea8026Sopenharmony_ci 11919ea8026Sopenharmony_ci // allocate our block array, all blocks start as uninitialized 12019ea8026Sopenharmony_ci bd->blocks = malloc(bd->cfg->erase_count * sizeof(lfs_emubd_block_t*)); 12119ea8026Sopenharmony_ci if (!bd->blocks) { 12219ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM); 12319ea8026Sopenharmony_ci return LFS_ERR_NOMEM; 12419ea8026Sopenharmony_ci } 12519ea8026Sopenharmony_ci memset(bd->blocks, 0, bd->cfg->erase_count * sizeof(lfs_emubd_block_t*)); 12619ea8026Sopenharmony_ci 12719ea8026Sopenharmony_ci // setup testing things 12819ea8026Sopenharmony_ci bd->readed = 0; 12919ea8026Sopenharmony_ci bd->proged = 0; 13019ea8026Sopenharmony_ci bd->erased = 0; 13119ea8026Sopenharmony_ci bd->power_cycles = bd->cfg->power_cycles; 13219ea8026Sopenharmony_ci bd->disk = NULL; 13319ea8026Sopenharmony_ci 13419ea8026Sopenharmony_ci if (bd->cfg->disk_path) { 13519ea8026Sopenharmony_ci bd->disk = malloc(sizeof(lfs_emubd_disk_t)); 13619ea8026Sopenharmony_ci if (!bd->disk) { 13719ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM); 13819ea8026Sopenharmony_ci return LFS_ERR_NOMEM; 13919ea8026Sopenharmony_ci } 14019ea8026Sopenharmony_ci bd->disk->rc = 1; 14119ea8026Sopenharmony_ci bd->disk->scratch = NULL; 14219ea8026Sopenharmony_ci 14319ea8026Sopenharmony_ci #ifdef _WIN32 14419ea8026Sopenharmony_ci bd->disk->fd = open(bd->cfg->disk_path, 14519ea8026Sopenharmony_ci O_RDWR | O_CREAT | O_BINARY, 0666); 14619ea8026Sopenharmony_ci #else 14719ea8026Sopenharmony_ci bd->disk->fd = open(bd->cfg->disk_path, 14819ea8026Sopenharmony_ci O_RDWR | O_CREAT, 0666); 14919ea8026Sopenharmony_ci #endif 15019ea8026Sopenharmony_ci if (bd->disk->fd < 0) { 15119ea8026Sopenharmony_ci int err = -errno; 15219ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err); 15319ea8026Sopenharmony_ci return err; 15419ea8026Sopenharmony_ci } 15519ea8026Sopenharmony_ci 15619ea8026Sopenharmony_ci // if we're emulating erase values, we can keep a block around in 15719ea8026Sopenharmony_ci // memory of just the erase state to speed up emulated erases 15819ea8026Sopenharmony_ci if (bd->cfg->erase_value != -1) { 15919ea8026Sopenharmony_ci bd->disk->scratch = malloc(bd->cfg->erase_size); 16019ea8026Sopenharmony_ci if (!bd->disk->scratch) { 16119ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM); 16219ea8026Sopenharmony_ci return LFS_ERR_NOMEM; 16319ea8026Sopenharmony_ci } 16419ea8026Sopenharmony_ci memset(bd->disk->scratch, 16519ea8026Sopenharmony_ci bd->cfg->erase_value, 16619ea8026Sopenharmony_ci bd->cfg->erase_size); 16719ea8026Sopenharmony_ci 16819ea8026Sopenharmony_ci // go ahead and erase all of the disk, otherwise the file will not 16919ea8026Sopenharmony_ci // match our internal representation 17019ea8026Sopenharmony_ci for (size_t i = 0; i < bd->cfg->erase_count; i++) { 17119ea8026Sopenharmony_ci ssize_t res = write(bd->disk->fd, 17219ea8026Sopenharmony_ci bd->disk->scratch, 17319ea8026Sopenharmony_ci bd->cfg->erase_size); 17419ea8026Sopenharmony_ci if (res < 0) { 17519ea8026Sopenharmony_ci int err = -errno; 17619ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err); 17719ea8026Sopenharmony_ci return err; 17819ea8026Sopenharmony_ci } 17919ea8026Sopenharmony_ci } 18019ea8026Sopenharmony_ci } 18119ea8026Sopenharmony_ci } 18219ea8026Sopenharmony_ci 18319ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_create -> %d", 0); 18419ea8026Sopenharmony_ci return 0; 18519ea8026Sopenharmony_ci} 18619ea8026Sopenharmony_ci 18719ea8026Sopenharmony_ciint lfs_emubd_destroy(const struct lfs_config *cfg) { 18819ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_destroy(%p)", (void*)cfg); 18919ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 19019ea8026Sopenharmony_ci 19119ea8026Sopenharmony_ci // decrement reference counts 19219ea8026Sopenharmony_ci for (lfs_block_t i = 0; i < bd->cfg->erase_count; i++) { 19319ea8026Sopenharmony_ci lfs_emubd_decblock(bd->blocks[i]); 19419ea8026Sopenharmony_ci } 19519ea8026Sopenharmony_ci free(bd->blocks); 19619ea8026Sopenharmony_ci 19719ea8026Sopenharmony_ci // clean up other resources 19819ea8026Sopenharmony_ci if (bd->disk) { 19919ea8026Sopenharmony_ci bd->disk->rc -= 1; 20019ea8026Sopenharmony_ci if (bd->disk->rc == 0) { 20119ea8026Sopenharmony_ci close(bd->disk->fd); 20219ea8026Sopenharmony_ci free(bd->disk->scratch); 20319ea8026Sopenharmony_ci free(bd->disk); 20419ea8026Sopenharmony_ci } 20519ea8026Sopenharmony_ci } 20619ea8026Sopenharmony_ci 20719ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_destroy -> %d", 0); 20819ea8026Sopenharmony_ci return 0; 20919ea8026Sopenharmony_ci} 21019ea8026Sopenharmony_ci 21119ea8026Sopenharmony_ci 21219ea8026Sopenharmony_ci 21319ea8026Sopenharmony_ci// block device API 21419ea8026Sopenharmony_ci 21519ea8026Sopenharmony_ciint lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block, 21619ea8026Sopenharmony_ci lfs_off_t off, void *buffer, lfs_size_t size) { 21719ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_read(%p, " 21819ea8026Sopenharmony_ci "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", 21919ea8026Sopenharmony_ci (void*)cfg, block, off, buffer, size); 22019ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 22119ea8026Sopenharmony_ci 22219ea8026Sopenharmony_ci // check if read is valid 22319ea8026Sopenharmony_ci LFS_ASSERT(block < bd->cfg->erase_count); 22419ea8026Sopenharmony_ci LFS_ASSERT(off % bd->cfg->read_size == 0); 22519ea8026Sopenharmony_ci LFS_ASSERT(size % bd->cfg->read_size == 0); 22619ea8026Sopenharmony_ci LFS_ASSERT(off+size <= bd->cfg->erase_size); 22719ea8026Sopenharmony_ci 22819ea8026Sopenharmony_ci // get the block 22919ea8026Sopenharmony_ci const lfs_emubd_block_t *b = bd->blocks[block]; 23019ea8026Sopenharmony_ci if (b) { 23119ea8026Sopenharmony_ci // block bad? 23219ea8026Sopenharmony_ci if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles && 23319ea8026Sopenharmony_ci bd->cfg->badblock_behavior == LFS_EMUBD_BADBLOCK_READERROR) { 23419ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_read -> %d", LFS_ERR_CORRUPT); 23519ea8026Sopenharmony_ci return LFS_ERR_CORRUPT; 23619ea8026Sopenharmony_ci } 23719ea8026Sopenharmony_ci 23819ea8026Sopenharmony_ci // read data 23919ea8026Sopenharmony_ci memcpy(buffer, &b->data[off], size); 24019ea8026Sopenharmony_ci } else { 24119ea8026Sopenharmony_ci // zero for consistency 24219ea8026Sopenharmony_ci memset(buffer, 24319ea8026Sopenharmony_ci (bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0, 24419ea8026Sopenharmony_ci size); 24519ea8026Sopenharmony_ci } 24619ea8026Sopenharmony_ci 24719ea8026Sopenharmony_ci // track reads 24819ea8026Sopenharmony_ci bd->readed += size; 24919ea8026Sopenharmony_ci if (bd->cfg->read_sleep) { 25019ea8026Sopenharmony_ci int err = nanosleep(&(struct timespec){ 25119ea8026Sopenharmony_ci .tv_sec=bd->cfg->read_sleep/1000000000, 25219ea8026Sopenharmony_ci .tv_nsec=bd->cfg->read_sleep%1000000000}, 25319ea8026Sopenharmony_ci NULL); 25419ea8026Sopenharmony_ci if (err) { 25519ea8026Sopenharmony_ci err = -errno; 25619ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_read -> %d", err); 25719ea8026Sopenharmony_ci return err; 25819ea8026Sopenharmony_ci } 25919ea8026Sopenharmony_ci } 26019ea8026Sopenharmony_ci 26119ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_read -> %d", 0); 26219ea8026Sopenharmony_ci return 0; 26319ea8026Sopenharmony_ci} 26419ea8026Sopenharmony_ci 26519ea8026Sopenharmony_ciint lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, 26619ea8026Sopenharmony_ci lfs_off_t off, const void *buffer, lfs_size_t size) { 26719ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_prog(%p, " 26819ea8026Sopenharmony_ci "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", 26919ea8026Sopenharmony_ci (void*)cfg, block, off, buffer, size); 27019ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 27119ea8026Sopenharmony_ci 27219ea8026Sopenharmony_ci // check if write is valid 27319ea8026Sopenharmony_ci LFS_ASSERT(block < bd->cfg->erase_count); 27419ea8026Sopenharmony_ci LFS_ASSERT(off % bd->cfg->prog_size == 0); 27519ea8026Sopenharmony_ci LFS_ASSERT(size % bd->cfg->prog_size == 0); 27619ea8026Sopenharmony_ci LFS_ASSERT(off+size <= bd->cfg->erase_size); 27719ea8026Sopenharmony_ci 27819ea8026Sopenharmony_ci // get the block 27919ea8026Sopenharmony_ci lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]); 28019ea8026Sopenharmony_ci if (!b) { 28119ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_NOMEM); 28219ea8026Sopenharmony_ci return LFS_ERR_NOMEM; 28319ea8026Sopenharmony_ci } 28419ea8026Sopenharmony_ci 28519ea8026Sopenharmony_ci // block bad? 28619ea8026Sopenharmony_ci if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles) { 28719ea8026Sopenharmony_ci if (bd->cfg->badblock_behavior == 28819ea8026Sopenharmony_ci LFS_EMUBD_BADBLOCK_PROGERROR) { 28919ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_CORRUPT); 29019ea8026Sopenharmony_ci return LFS_ERR_CORRUPT; 29119ea8026Sopenharmony_ci } else if (bd->cfg->badblock_behavior == 29219ea8026Sopenharmony_ci LFS_EMUBD_BADBLOCK_PROGNOOP || 29319ea8026Sopenharmony_ci bd->cfg->badblock_behavior == 29419ea8026Sopenharmony_ci LFS_EMUBD_BADBLOCK_ERASENOOP) { 29519ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0); 29619ea8026Sopenharmony_ci return 0; 29719ea8026Sopenharmony_ci } 29819ea8026Sopenharmony_ci } 29919ea8026Sopenharmony_ci 30019ea8026Sopenharmony_ci // were we erased properly? 30119ea8026Sopenharmony_ci if (bd->cfg->erase_value != -1) { 30219ea8026Sopenharmony_ci for (lfs_off_t i = 0; i < size; i++) { 30319ea8026Sopenharmony_ci LFS_ASSERT(b->data[off+i] == bd->cfg->erase_value); 30419ea8026Sopenharmony_ci } 30519ea8026Sopenharmony_ci } 30619ea8026Sopenharmony_ci 30719ea8026Sopenharmony_ci // prog data 30819ea8026Sopenharmony_ci memcpy(&b->data[off], buffer, size); 30919ea8026Sopenharmony_ci 31019ea8026Sopenharmony_ci // mirror to disk file? 31119ea8026Sopenharmony_ci if (bd->disk) { 31219ea8026Sopenharmony_ci off_t res1 = lseek(bd->disk->fd, 31319ea8026Sopenharmony_ci (off_t)block*bd->cfg->erase_size + (off_t)off, 31419ea8026Sopenharmony_ci SEEK_SET); 31519ea8026Sopenharmony_ci if (res1 < 0) { 31619ea8026Sopenharmony_ci int err = -errno; 31719ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err); 31819ea8026Sopenharmony_ci return err; 31919ea8026Sopenharmony_ci } 32019ea8026Sopenharmony_ci 32119ea8026Sopenharmony_ci ssize_t res2 = write(bd->disk->fd, buffer, size); 32219ea8026Sopenharmony_ci if (res2 < 0) { 32319ea8026Sopenharmony_ci int err = -errno; 32419ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err); 32519ea8026Sopenharmony_ci return err; 32619ea8026Sopenharmony_ci } 32719ea8026Sopenharmony_ci } 32819ea8026Sopenharmony_ci 32919ea8026Sopenharmony_ci // track progs 33019ea8026Sopenharmony_ci bd->proged += size; 33119ea8026Sopenharmony_ci if (bd->cfg->prog_sleep) { 33219ea8026Sopenharmony_ci int err = nanosleep(&(struct timespec){ 33319ea8026Sopenharmony_ci .tv_sec=bd->cfg->prog_sleep/1000000000, 33419ea8026Sopenharmony_ci .tv_nsec=bd->cfg->prog_sleep%1000000000}, 33519ea8026Sopenharmony_ci NULL); 33619ea8026Sopenharmony_ci if (err) { 33719ea8026Sopenharmony_ci err = -errno; 33819ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err); 33919ea8026Sopenharmony_ci return err; 34019ea8026Sopenharmony_ci } 34119ea8026Sopenharmony_ci } 34219ea8026Sopenharmony_ci 34319ea8026Sopenharmony_ci // lose power? 34419ea8026Sopenharmony_ci if (bd->power_cycles > 0) { 34519ea8026Sopenharmony_ci bd->power_cycles -= 1; 34619ea8026Sopenharmony_ci if (bd->power_cycles == 0) { 34719ea8026Sopenharmony_ci // simulate power loss 34819ea8026Sopenharmony_ci bd->cfg->powerloss_cb(bd->cfg->powerloss_data); 34919ea8026Sopenharmony_ci } 35019ea8026Sopenharmony_ci } 35119ea8026Sopenharmony_ci 35219ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0); 35319ea8026Sopenharmony_ci return 0; 35419ea8026Sopenharmony_ci} 35519ea8026Sopenharmony_ci 35619ea8026Sopenharmony_ciint lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { 35719ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_erase(%p, 0x%"PRIx32" (%"PRIu32"))", 35819ea8026Sopenharmony_ci (void*)cfg, block, ((lfs_emubd_t*)cfg->context)->cfg->erase_size); 35919ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 36019ea8026Sopenharmony_ci 36119ea8026Sopenharmony_ci // check if erase is valid 36219ea8026Sopenharmony_ci LFS_ASSERT(block < bd->cfg->erase_count); 36319ea8026Sopenharmony_ci 36419ea8026Sopenharmony_ci // get the block 36519ea8026Sopenharmony_ci lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]); 36619ea8026Sopenharmony_ci if (!b) { 36719ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_NOMEM); 36819ea8026Sopenharmony_ci return LFS_ERR_NOMEM; 36919ea8026Sopenharmony_ci } 37019ea8026Sopenharmony_ci 37119ea8026Sopenharmony_ci // block bad? 37219ea8026Sopenharmony_ci if (bd->cfg->erase_cycles) { 37319ea8026Sopenharmony_ci if (b->wear >= bd->cfg->erase_cycles) { 37419ea8026Sopenharmony_ci if (bd->cfg->badblock_behavior == 37519ea8026Sopenharmony_ci LFS_EMUBD_BADBLOCK_ERASEERROR) { 37619ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_CORRUPT); 37719ea8026Sopenharmony_ci return LFS_ERR_CORRUPT; 37819ea8026Sopenharmony_ci } else if (bd->cfg->badblock_behavior == 37919ea8026Sopenharmony_ci LFS_EMUBD_BADBLOCK_ERASENOOP) { 38019ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0); 38119ea8026Sopenharmony_ci return 0; 38219ea8026Sopenharmony_ci } 38319ea8026Sopenharmony_ci } else { 38419ea8026Sopenharmony_ci // mark wear 38519ea8026Sopenharmony_ci b->wear += 1; 38619ea8026Sopenharmony_ci } 38719ea8026Sopenharmony_ci } 38819ea8026Sopenharmony_ci 38919ea8026Sopenharmony_ci // emulate an erase value? 39019ea8026Sopenharmony_ci if (bd->cfg->erase_value != -1) { 39119ea8026Sopenharmony_ci memset(b->data, bd->cfg->erase_value, bd->cfg->erase_size); 39219ea8026Sopenharmony_ci 39319ea8026Sopenharmony_ci // mirror to disk file? 39419ea8026Sopenharmony_ci if (bd->disk) { 39519ea8026Sopenharmony_ci off_t res1 = lseek(bd->disk->fd, 39619ea8026Sopenharmony_ci (off_t)block*bd->cfg->erase_size, 39719ea8026Sopenharmony_ci SEEK_SET); 39819ea8026Sopenharmony_ci if (res1 < 0) { 39919ea8026Sopenharmony_ci int err = -errno; 40019ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err); 40119ea8026Sopenharmony_ci return err; 40219ea8026Sopenharmony_ci } 40319ea8026Sopenharmony_ci 40419ea8026Sopenharmony_ci ssize_t res2 = write(bd->disk->fd, 40519ea8026Sopenharmony_ci bd->disk->scratch, 40619ea8026Sopenharmony_ci bd->cfg->erase_size); 40719ea8026Sopenharmony_ci if (res2 < 0) { 40819ea8026Sopenharmony_ci int err = -errno; 40919ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err); 41019ea8026Sopenharmony_ci return err; 41119ea8026Sopenharmony_ci } 41219ea8026Sopenharmony_ci } 41319ea8026Sopenharmony_ci } 41419ea8026Sopenharmony_ci 41519ea8026Sopenharmony_ci // track erases 41619ea8026Sopenharmony_ci bd->erased += bd->cfg->erase_size; 41719ea8026Sopenharmony_ci if (bd->cfg->erase_sleep) { 41819ea8026Sopenharmony_ci int err = nanosleep(&(struct timespec){ 41919ea8026Sopenharmony_ci .tv_sec=bd->cfg->erase_sleep/1000000000, 42019ea8026Sopenharmony_ci .tv_nsec=bd->cfg->erase_sleep%1000000000}, 42119ea8026Sopenharmony_ci NULL); 42219ea8026Sopenharmony_ci if (err) { 42319ea8026Sopenharmony_ci err = -errno; 42419ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err); 42519ea8026Sopenharmony_ci return err; 42619ea8026Sopenharmony_ci } 42719ea8026Sopenharmony_ci } 42819ea8026Sopenharmony_ci 42919ea8026Sopenharmony_ci // lose power? 43019ea8026Sopenharmony_ci if (bd->power_cycles > 0) { 43119ea8026Sopenharmony_ci bd->power_cycles -= 1; 43219ea8026Sopenharmony_ci if (bd->power_cycles == 0) { 43319ea8026Sopenharmony_ci // simulate power loss 43419ea8026Sopenharmony_ci bd->cfg->powerloss_cb(bd->cfg->powerloss_data); 43519ea8026Sopenharmony_ci } 43619ea8026Sopenharmony_ci } 43719ea8026Sopenharmony_ci 43819ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0); 43919ea8026Sopenharmony_ci return 0; 44019ea8026Sopenharmony_ci} 44119ea8026Sopenharmony_ci 44219ea8026Sopenharmony_ciint lfs_emubd_sync(const struct lfs_config *cfg) { 44319ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_sync(%p)", (void*)cfg); 44419ea8026Sopenharmony_ci 44519ea8026Sopenharmony_ci // do nothing 44619ea8026Sopenharmony_ci (void)cfg; 44719ea8026Sopenharmony_ci 44819ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_sync -> %d", 0); 44919ea8026Sopenharmony_ci return 0; 45019ea8026Sopenharmony_ci} 45119ea8026Sopenharmony_ci 45219ea8026Sopenharmony_ci/// Additional extended API for driving test features /// 45319ea8026Sopenharmony_ci 45419ea8026Sopenharmony_cistatic int lfs_emubd_rawcrc(const struct lfs_config *cfg, 45519ea8026Sopenharmony_ci lfs_block_t block, uint32_t *crc) { 45619ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 45719ea8026Sopenharmony_ci 45819ea8026Sopenharmony_ci // check if crc is valid 45919ea8026Sopenharmony_ci LFS_ASSERT(block < cfg->block_count); 46019ea8026Sopenharmony_ci 46119ea8026Sopenharmony_ci // crc the block 46219ea8026Sopenharmony_ci uint32_t crc_ = 0xffffffff; 46319ea8026Sopenharmony_ci const lfs_emubd_block_t *b = bd->blocks[block]; 46419ea8026Sopenharmony_ci if (b) { 46519ea8026Sopenharmony_ci crc_ = lfs_crc(crc_, b->data, cfg->block_size); 46619ea8026Sopenharmony_ci } else { 46719ea8026Sopenharmony_ci uint8_t erase_value = (bd->cfg->erase_value != -1) 46819ea8026Sopenharmony_ci ? bd->cfg->erase_value 46919ea8026Sopenharmony_ci : 0; 47019ea8026Sopenharmony_ci for (lfs_size_t i = 0; i < cfg->block_size; i++) { 47119ea8026Sopenharmony_ci crc_ = lfs_crc(crc_, &erase_value, 1); 47219ea8026Sopenharmony_ci } 47319ea8026Sopenharmony_ci } 47419ea8026Sopenharmony_ci *crc = 0xffffffff ^ crc_; 47519ea8026Sopenharmony_ci 47619ea8026Sopenharmony_ci return 0; 47719ea8026Sopenharmony_ci} 47819ea8026Sopenharmony_ci 47919ea8026Sopenharmony_ciint lfs_emubd_crc(const struct lfs_config *cfg, 48019ea8026Sopenharmony_ci lfs_block_t block, uint32_t *crc) { 48119ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_crc(%p, %"PRIu32", %p)", 48219ea8026Sopenharmony_ci (void*)cfg, block, crc); 48319ea8026Sopenharmony_ci int err = lfs_emubd_rawcrc(cfg, block, crc); 48419ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_crc -> %d", err); 48519ea8026Sopenharmony_ci return err; 48619ea8026Sopenharmony_ci} 48719ea8026Sopenharmony_ci 48819ea8026Sopenharmony_ciint lfs_emubd_bdcrc(const struct lfs_config *cfg, uint32_t *crc) { 48919ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_bdcrc(%p, %p)", (void*)cfg, crc); 49019ea8026Sopenharmony_ci 49119ea8026Sopenharmony_ci uint32_t crc_ = 0xffffffff; 49219ea8026Sopenharmony_ci for (lfs_block_t i = 0; i < cfg->block_count; i++) { 49319ea8026Sopenharmony_ci uint32_t i_crc; 49419ea8026Sopenharmony_ci int err = lfs_emubd_rawcrc(cfg, i, &i_crc); 49519ea8026Sopenharmony_ci if (err) { 49619ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_bdcrc -> %d", err); 49719ea8026Sopenharmony_ci return err; 49819ea8026Sopenharmony_ci } 49919ea8026Sopenharmony_ci 50019ea8026Sopenharmony_ci crc_ = lfs_crc(crc_, &i_crc, sizeof(uint32_t)); 50119ea8026Sopenharmony_ci } 50219ea8026Sopenharmony_ci *crc = 0xffffffff ^ crc_; 50319ea8026Sopenharmony_ci 50419ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_bdcrc -> %d", 0); 50519ea8026Sopenharmony_ci return 0; 50619ea8026Sopenharmony_ci} 50719ea8026Sopenharmony_ci 50819ea8026Sopenharmony_cilfs_emubd_sio_t lfs_emubd_readed(const struct lfs_config *cfg) { 50919ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_readed(%p)", (void*)cfg); 51019ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 51119ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_readed -> %"PRIu64, bd->readed); 51219ea8026Sopenharmony_ci return bd->readed; 51319ea8026Sopenharmony_ci} 51419ea8026Sopenharmony_ci 51519ea8026Sopenharmony_cilfs_emubd_sio_t lfs_emubd_proged(const struct lfs_config *cfg) { 51619ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_proged(%p)", (void*)cfg); 51719ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 51819ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_proged -> %"PRIu64, bd->proged); 51919ea8026Sopenharmony_ci return bd->proged; 52019ea8026Sopenharmony_ci} 52119ea8026Sopenharmony_ci 52219ea8026Sopenharmony_cilfs_emubd_sio_t lfs_emubd_erased(const struct lfs_config *cfg) { 52319ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_erased(%p)", (void*)cfg); 52419ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 52519ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_erased -> %"PRIu64, bd->erased); 52619ea8026Sopenharmony_ci return bd->erased; 52719ea8026Sopenharmony_ci} 52819ea8026Sopenharmony_ci 52919ea8026Sopenharmony_ciint lfs_emubd_setreaded(const struct lfs_config *cfg, lfs_emubd_io_t readed) { 53019ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_setreaded(%p, %"PRIu64")", (void*)cfg, readed); 53119ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 53219ea8026Sopenharmony_ci bd->readed = readed; 53319ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_setreaded -> %d", 0); 53419ea8026Sopenharmony_ci return 0; 53519ea8026Sopenharmony_ci} 53619ea8026Sopenharmony_ci 53719ea8026Sopenharmony_ciint lfs_emubd_setproged(const struct lfs_config *cfg, lfs_emubd_io_t proged) { 53819ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_setproged(%p, %"PRIu64")", (void*)cfg, proged); 53919ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 54019ea8026Sopenharmony_ci bd->proged = proged; 54119ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_setproged -> %d", 0); 54219ea8026Sopenharmony_ci return 0; 54319ea8026Sopenharmony_ci} 54419ea8026Sopenharmony_ci 54519ea8026Sopenharmony_ciint lfs_emubd_seterased(const struct lfs_config *cfg, lfs_emubd_io_t erased) { 54619ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_seterased(%p, %"PRIu64")", (void*)cfg, erased); 54719ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 54819ea8026Sopenharmony_ci bd->erased = erased; 54919ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_seterased -> %d", 0); 55019ea8026Sopenharmony_ci return 0; 55119ea8026Sopenharmony_ci} 55219ea8026Sopenharmony_ci 55319ea8026Sopenharmony_cilfs_emubd_swear_t lfs_emubd_wear(const struct lfs_config *cfg, 55419ea8026Sopenharmony_ci lfs_block_t block) { 55519ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_wear(%p, %"PRIu32")", (void*)cfg, block); 55619ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 55719ea8026Sopenharmony_ci 55819ea8026Sopenharmony_ci // check if block is valid 55919ea8026Sopenharmony_ci LFS_ASSERT(block < bd->cfg->erase_count); 56019ea8026Sopenharmony_ci 56119ea8026Sopenharmony_ci // get the wear 56219ea8026Sopenharmony_ci lfs_emubd_wear_t wear; 56319ea8026Sopenharmony_ci const lfs_emubd_block_t *b = bd->blocks[block]; 56419ea8026Sopenharmony_ci if (b) { 56519ea8026Sopenharmony_ci wear = b->wear; 56619ea8026Sopenharmony_ci } else { 56719ea8026Sopenharmony_ci wear = 0; 56819ea8026Sopenharmony_ci } 56919ea8026Sopenharmony_ci 57019ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_wear -> %"PRIi32, wear); 57119ea8026Sopenharmony_ci return wear; 57219ea8026Sopenharmony_ci} 57319ea8026Sopenharmony_ci 57419ea8026Sopenharmony_ciint lfs_emubd_setwear(const struct lfs_config *cfg, 57519ea8026Sopenharmony_ci lfs_block_t block, lfs_emubd_wear_t wear) { 57619ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_setwear(%p, %"PRIu32", %"PRIi32")", 57719ea8026Sopenharmony_ci (void*)cfg, block, wear); 57819ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 57919ea8026Sopenharmony_ci 58019ea8026Sopenharmony_ci // check if block is valid 58119ea8026Sopenharmony_ci LFS_ASSERT(block < bd->cfg->erase_count); 58219ea8026Sopenharmony_ci 58319ea8026Sopenharmony_ci // set the wear 58419ea8026Sopenharmony_ci lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]); 58519ea8026Sopenharmony_ci if (!b) { 58619ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", LFS_ERR_NOMEM); 58719ea8026Sopenharmony_ci return LFS_ERR_NOMEM; 58819ea8026Sopenharmony_ci } 58919ea8026Sopenharmony_ci b->wear = wear; 59019ea8026Sopenharmony_ci 59119ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", 0); 59219ea8026Sopenharmony_ci return 0; 59319ea8026Sopenharmony_ci} 59419ea8026Sopenharmony_ci 59519ea8026Sopenharmony_cilfs_emubd_spowercycles_t lfs_emubd_powercycles( 59619ea8026Sopenharmony_ci const struct lfs_config *cfg) { 59719ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_powercycles(%p)", (void*)cfg); 59819ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 59919ea8026Sopenharmony_ci 60019ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_powercycles -> %"PRIi32, bd->power_cycles); 60119ea8026Sopenharmony_ci return bd->power_cycles; 60219ea8026Sopenharmony_ci} 60319ea8026Sopenharmony_ci 60419ea8026Sopenharmony_ciint lfs_emubd_setpowercycles(const struct lfs_config *cfg, 60519ea8026Sopenharmony_ci lfs_emubd_powercycles_t power_cycles) { 60619ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_setpowercycles(%p, %"PRIi32")", 60719ea8026Sopenharmony_ci (void*)cfg, power_cycles); 60819ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 60919ea8026Sopenharmony_ci 61019ea8026Sopenharmony_ci bd->power_cycles = power_cycles; 61119ea8026Sopenharmony_ci 61219ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_powercycles -> %d", 0); 61319ea8026Sopenharmony_ci return 0; 61419ea8026Sopenharmony_ci} 61519ea8026Sopenharmony_ci 61619ea8026Sopenharmony_ciint lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy) { 61719ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_copy(%p, %p)", (void*)cfg, (void*)copy); 61819ea8026Sopenharmony_ci lfs_emubd_t *bd = cfg->context; 61919ea8026Sopenharmony_ci 62019ea8026Sopenharmony_ci // lazily copy over our block array 62119ea8026Sopenharmony_ci copy->blocks = malloc(bd->cfg->erase_count * sizeof(lfs_emubd_block_t*)); 62219ea8026Sopenharmony_ci if (!copy->blocks) { 62319ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", LFS_ERR_NOMEM); 62419ea8026Sopenharmony_ci return LFS_ERR_NOMEM; 62519ea8026Sopenharmony_ci } 62619ea8026Sopenharmony_ci 62719ea8026Sopenharmony_ci for (size_t i = 0; i < bd->cfg->erase_count; i++) { 62819ea8026Sopenharmony_ci copy->blocks[i] = lfs_emubd_incblock(bd->blocks[i]); 62919ea8026Sopenharmony_ci } 63019ea8026Sopenharmony_ci 63119ea8026Sopenharmony_ci // other state 63219ea8026Sopenharmony_ci copy->readed = bd->readed; 63319ea8026Sopenharmony_ci copy->proged = bd->proged; 63419ea8026Sopenharmony_ci copy->erased = bd->erased; 63519ea8026Sopenharmony_ci copy->power_cycles = bd->power_cycles; 63619ea8026Sopenharmony_ci copy->disk = bd->disk; 63719ea8026Sopenharmony_ci if (copy->disk) { 63819ea8026Sopenharmony_ci copy->disk->rc += 1; 63919ea8026Sopenharmony_ci } 64019ea8026Sopenharmony_ci copy->cfg = bd->cfg; 64119ea8026Sopenharmony_ci 64219ea8026Sopenharmony_ci LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", 0); 64319ea8026Sopenharmony_ci return 0; 64419ea8026Sopenharmony_ci} 64519ea8026Sopenharmony_ci 646