1 /*
2  * Emulating block device, wraps filebd and rambd while providing a bunch
3  * of hooks for testing littlefs in various conditions.
4  *
5  * Copyright (c) 2022, The littlefs authors.
6  * Copyright (c) 2017, Arm Limited. All rights reserved.
7  * SPDX-License-Identifier: BSD-3-Clause
8  */
9 #ifndef LFS_EMUBD_H
10 #define LFS_EMUBD_H
11 
12 #include "lfs.h"
13 #include "lfs_util.h"
14 #include "bd/lfs_rambd.h"
15 #include "bd/lfs_filebd.h"
16 
17 #ifdef __cplusplus
18 extern "C"
19 {
20 #endif
21 
22 
23 // Block device specific tracing
24 #ifndef LFS_EMUBD_TRACE
25 #ifdef LFS_EMUBD_YES_TRACE
26 #define LFS_EMUBD_TRACE(...) LFS_TRACE(__VA_ARGS__)
27 #else
28 #define LFS_EMUBD_TRACE(...)
29 #endif
30 #endif
31 
32 // Mode determining how "bad-blocks" behave during testing. This simulates
33 // some real-world circumstances such as progs not sticking (prog-noop),
34 // a readonly disk (erase-noop), and ECC failures (read-error).
35 //
36 // Not that read-noop is not allowed. Read _must_ return a consistent (but
37 // may be arbitrary) value on every read.
38 typedef enum lfs_emubd_badblock_behavior {
39     LFS_EMUBD_BADBLOCK_PROGERROR,
40     LFS_EMUBD_BADBLOCK_ERASEERROR,
41     LFS_EMUBD_BADBLOCK_READERROR,
42     LFS_EMUBD_BADBLOCK_PROGNOOP,
43     LFS_EMUBD_BADBLOCK_ERASENOOP,
44 } lfs_emubd_badblock_behavior_t;
45 
46 // Mode determining how power-loss behaves during testing. For now this
47 // only supports a noop behavior, leaving the data on-disk untouched.
48 typedef enum lfs_emubd_powerloss_behavior {
49     LFS_EMUBD_POWERLOSS_NOOP,
50 } lfs_emubd_powerloss_behavior_t;
51 
52 // Type for measuring read/program/erase operations
53 typedef uint64_t lfs_emubd_io_t;
54 typedef int64_t lfs_emubd_sio_t;
55 
56 // Type for measuring wear
57 typedef uint32_t lfs_emubd_wear_t;
58 typedef int32_t lfs_emubd_swear_t;
59 
60 // Type for tracking power-cycles
61 typedef uint32_t lfs_emubd_powercycles_t;
62 typedef int32_t lfs_emubd_spowercycles_t;
63 
64 // Type for delays in nanoseconds
65 typedef uint64_t lfs_emubd_sleep_t;
66 typedef int64_t lfs_emubd_ssleep_t;
67 
68 // emubd config, this is required for testing
69 struct lfs_emubd_config {
70     // Minimum size of a read operation in bytes.
71     lfs_size_t read_size;
72 
73     // Minimum size of a program operation in bytes.
74     lfs_size_t prog_size;
75 
76     // Size of an erase operation in bytes.
77     lfs_size_t erase_size;
78 
79     // Number of erase blocks on the device.
80     lfs_size_t erase_count;
81 
82     // 8-bit erase value to use for simulating erases. -1 does not simulate
83     // erases, which can speed up testing by avoiding the extra block-device
84     // operations to store the erase value.
85     int32_t erase_value;
86 
87     // Number of erase cycles before a block becomes "bad". The exact behavior
88     // of bad blocks is controlled by badblock_behavior.
89     uint32_t erase_cycles;
90 
91     // The mode determining how bad-blocks fail
92     lfs_emubd_badblock_behavior_t badblock_behavior;
93 
94     // Number of write operations (erase/prog) before triggering a power-loss.
95     // power_cycles=0 disables this. The exact behavior of power-loss is
96     // controlled by a combination of powerloss_behavior and powerloss_cb.
97     lfs_emubd_powercycles_t power_cycles;
98 
99     // The mode determining how power-loss affects disk
100     lfs_emubd_powerloss_behavior_t powerloss_behavior;
101 
102     // Function to call to emulate power-loss. The exact behavior of power-loss
103     // is up to the runner to provide.
104     void (*powerloss_cb)(void*);
105 
106     // Data for power-loss callback
107     void *powerloss_data;
108 
109     // True to track when power-loss could have occured. Note this involves
110     // heavy memory usage!
111     bool track_branches;
112 
113     // Path to file to use as a mirror of the disk. This provides a way to view
114     // the current state of the block device.
115     const char *disk_path;
116 
117     // Artificial delay in nanoseconds, there is no purpose for this other
118     // than slowing down the simulation.
119     lfs_emubd_sleep_t read_sleep;
120 
121     // Artificial delay in nanoseconds, there is no purpose for this other
122     // than slowing down the simulation.
123     lfs_emubd_sleep_t prog_sleep;
124 
125     // Artificial delay in nanoseconds, there is no purpose for this other
126     // than slowing down the simulation.
127     lfs_emubd_sleep_t erase_sleep;
128 };
129 
130 // A reference counted block
131 typedef struct lfs_emubd_block {
132     uint32_t rc;
133     lfs_emubd_wear_t wear;
134 
135     uint8_t data[];
136 } lfs_emubd_block_t;
137 
138 // Disk mirror
139 typedef struct lfs_emubd_disk {
140     uint32_t rc;
141     int fd;
142     uint8_t *scratch;
143 } lfs_emubd_disk_t;
144 
145 // emubd state
146 typedef struct lfs_emubd {
147     // array of copy-on-write blocks
148     lfs_emubd_block_t **blocks;
149 
150     // some other test state
151     lfs_emubd_io_t readed;
152     lfs_emubd_io_t proged;
153     lfs_emubd_io_t erased;
154     lfs_emubd_powercycles_t power_cycles;
155     lfs_emubd_disk_t *disk;
156 
157     const struct lfs_emubd_config *cfg;
158 } lfs_emubd_t;
159 
160 
161 /// Block device API ///
162 
163 // Create an emulating block device using the geometry in lfs_config
164 int lfs_emubd_create(const struct lfs_config *cfg,
165         const struct lfs_emubd_config *bdcfg);
166 
167 // Clean up memory associated with block device
168 int lfs_emubd_destroy(const struct lfs_config *cfg);
169 
170 // Read a block
171 int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
172         lfs_off_t off, void *buffer, lfs_size_t size);
173 
174 // Program a block
175 //
176 // The block must have previously been erased.
177 int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
178         lfs_off_t off, const void *buffer, lfs_size_t size);
179 
180 // Erase a block
181 //
182 // A block must be erased before being programmed. The
183 // state of an erased block is undefined.
184 int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block);
185 
186 // Sync the block device
187 int lfs_emubd_sync(const struct lfs_config *cfg);
188 
189 
190 /// Additional extended API for driving test features ///
191 
192 // A CRC of a block for debugging purposes
193 int lfs_emubd_crc(const struct lfs_config *cfg,
194         lfs_block_t block, uint32_t *crc);
195 
196 // A CRC of the entire block device for debugging purposes
197 int lfs_emubd_bdcrc(const struct lfs_config *cfg, uint32_t *crc);
198 
199 // Get total amount of bytes read
200 lfs_emubd_sio_t lfs_emubd_readed(const struct lfs_config *cfg);
201 
202 // Get total amount of bytes programmed
203 lfs_emubd_sio_t lfs_emubd_proged(const struct lfs_config *cfg);
204 
205 // Get total amount of bytes erased
206 lfs_emubd_sio_t lfs_emubd_erased(const struct lfs_config *cfg);
207 
208 // Manually set amount of bytes read
209 int lfs_emubd_setreaded(const struct lfs_config *cfg, lfs_emubd_io_t readed);
210 
211 // Manually set amount of bytes programmed
212 int lfs_emubd_setproged(const struct lfs_config *cfg, lfs_emubd_io_t proged);
213 
214 // Manually set amount of bytes erased
215 int lfs_emubd_seterased(const struct lfs_config *cfg, lfs_emubd_io_t erased);
216 
217 // Get simulated wear on a given block
218 lfs_emubd_swear_t lfs_emubd_wear(const struct lfs_config *cfg,
219         lfs_block_t block);
220 
221 // Manually set simulated wear on a given block
222 int lfs_emubd_setwear(const struct lfs_config *cfg,
223         lfs_block_t block, lfs_emubd_wear_t wear);
224 
225 // Get the remaining power-cycles
226 lfs_emubd_spowercycles_t lfs_emubd_powercycles(
227         const struct lfs_config *cfg);
228 
229 // Manually set the remaining power-cycles
230 int lfs_emubd_setpowercycles(const struct lfs_config *cfg,
231         lfs_emubd_powercycles_t power_cycles);
232 
233 // Create a copy-on-write copy of the state of this block device
234 int lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy);
235 
236 
237 #ifdef __cplusplus
238 } /* extern "C" */
239 #endif
240 
241 #endif
242