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