qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

wear_leveling.c (30311B)


      1 // Copyright 2022 Nick Brassel (@tzarc)
      2 // SPDX-License-Identifier: GPL-2.0-or-later
      3 #include <stdbool.h>
      4 #include "fnv.h"
      5 #include "wear_leveling.h"
      6 #include "wear_leveling_drivers.h"
      7 #include "wear_leveling_internal.h"
      8 
      9 /*
     10     This wear leveling algorithm is adapted from algorithms from previous
     11     implementations in QMK, namely:
     12         - Artur F. (http://engsta.com/stm32-flash-memory-eeprom-emulator/)
     13         - Yiancar -- QMK's base implementation for STM32F303
     14         - Ilya Zhuravlev -- initial wear leveling algorithm
     15         - Don Kjer -- increased flash density algorithm
     16         - Nick Brassel (@tzarc) -- decoupled for use on other peripherals
     17 
     18     At this layer, it is assumed that any reads/writes from the backing store
     19     have a "reset state" after erasure of zero.
     20     It is up to the backing store to perform translation of values, such as
     21     taking the complement in order to deal with flash memory's reset value.
     22 
     23     Terminology:
     24 
     25         - Backing store: this is the storage area used by the wear leveling
     26             algorithm.
     27 
     28         - Backing size: this is the amount of storage provided by the backing
     29             store for use by the wear leveling algorithm.
     30 
     31         - Backing write size: this is the minimum number of bytes the backing
     32             store can write in a single operation.
     33 
     34         - Logical data: this is the externally-visible "emulated EEPROM" that
     35             external subsystems "see" when performing reads/writes.
     36 
     37         - Logical size: this is the amount of storage available for use
     38             externally. Effectively, the "size of the EEPROM".
     39 
     40         - Write log: this is a section of the backing store used to keep track
     41             of modifications without overwriting existing data. This log is
     42             "played back" on startup such that any subsequent reads are capable
     43             of returning the latest data.
     44 
     45         - Consolidated data: this is a section of the backing store reserved for
     46             use for the latest copy of logical data. This is only ever written
     47             when the write log is full -- the latest values for the logical data
     48             are written here and the write log is cleared.
     49 
     50     Configurables:
     51 
     52         - BACKING_STORE_WRITE_SIZE: The number of bytes requires for a write
     53             operation. This is defined by the capabilities of the backing store.
     54 
     55         - WEAR_LEVELING_BACKING_SIZE: The number of bytes provided by the
     56             backing store for use by the wear leveling algorithm.  This is
     57             defined by the capabilities of the backing store. This value must
     58             also be at least twice the size of the logical size, as well as a
     59             multiple of the logical size.
     60 
     61         - WEAR_LEVELING_LOGICAL_SIZE: The number of bytes externally visible
     62             to other subsystems performing reads/writes. This must be a multiple
     63             of the write size.
     64 
     65     General algorithm:
     66 
     67         During initialization:
     68             * The contents of the consolidated data section are read into cache.
     69             * The contents of the write log are "played back" and update the
     70                 cache accordingly.
     71 
     72         During reads:
     73             * Logical data is served from the cache.
     74 
     75         During writes:
     76             * The cache is updated with the new data.
     77             * A new write log entry is appended to the log.
     78             * If the log's full, data is consolidated and the write log cleared.
     79 
     80     Write log structure:
     81 
     82         The first 8 bytes of the write log are a FNV1a_64 hash of the contents
     83         of the consolidated data area, in an attempt to detect and guard against
     84         any data corruption.
     85 
     86         The write log follows the hash:
     87 
     88         Given that the algorithm needs to cater for 2-, 4-, and 8-byte writes,
     89         a variable-length write log entry is used such that the minimal amount
     90         of storage is used based off the backing store write size.
     91 
     92         Firstly, an empty log entry is expected to be all zeros. If the backing
     93         store uses 0xFF for cleared bytes, it should return the complement, such
     94         that this wear-leveling algorithm "receives" zeros.
     95 
     96         For multi-byte writes, up to 8 bytes will be used for each log entry,
     97         depending on the size of backing store writes:
     98 
     99         ╔ Multi-byte Log Entry (2, 4-byte) ═╗
    100         ║00XXXYYY║YYYYYYYY║YYYYYYYY║AAAAAAAA║
    101         ║  └┬┘└┬┘║└──┬───┘║└──┬───┘║└──┬───┘║
    102         ║  LenAdd║ Address║ Address║Value[0]║
    103         ╚════════╩════════╩════════╩════════╝
    104         ╔ Multi-byte Log Entry (2-byte) ══════════════════════╗
    105         ║00XXXYYY║YYYYYYYY║YYYYYYYY║AAAAAAAA║BBBBBBBB║CCCCCCCC║
    106         ║  └┬┘└┬┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║
    107         ║  LenAdd║ Address║ Address║Value[0]║Value[1]║Value[2]║
    108         ╚════════╩════════╩════════╩════════╩════════╩════════╝
    109         ╔ Multi-byte Log Entry (2, 4, 8-byte) ══════════════════════════════════╗
    110         ║00XXXYYY║YYYYYYYY║YYYYYYYY║AAAAAAAA║BBBBBBBB║CCCCCCCC║DDDDDDDD║EEEEEEEE║
    111         ║  └┬┘└┬┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║
    112         ║  LenAdd║ Address║ Address║Value[0]║Value[1]║Value[2]║Value[3]║Value[4]║
    113         ╚════════╩════════╩════════╩════════╩════════╩════════╩════════╩════════╝
    114 
    115         19 bits are used for the address, which allows for a max logical size of
    116         512kB. Up to 5 bytes can be included in a single log entry.
    117 
    118         For 2-byte backing store writes, the last two bytes are optional
    119             depending on the length of data to be written. Accordingly, either 3
    120             or 4 backing store write operations will occur.
    121         For 4-byte backing store writes, either one or two write operations
    122             occur, depending on the length.
    123         For 8-byte backing store writes, one write operation occur.
    124 
    125     2-byte backing store optimizations:
    126 
    127         For single byte writes, addresses between 0...63 are encoded in a single
    128         backing store write operation. 4- and 8-byte backing stores do not have
    129         this optimization as it does not minimize the number of bytes written.
    130 
    131         ╔ Byte-Entry ════╗
    132         ║01XXXXXXYYYYYYYY║
    133         ║  └─┬──┘└──┬───┘║
    134         ║ Address Value  ║
    135         ╚════════════════╝
    136         0 <= Address < 0x40 (64)
    137 
    138         A second optimization takes into account uint16_t writes of 0 or 1,
    139         specifically catering for KC_NO and KC_TRANSPARENT in the dynamic keymap
    140         subsystem. This is valid only for the first 16kB of logical data --
    141         addresses outside this range will use the multi-byte encoding above.
    142 
    143         ╔ U16-Encoded 0 ═╗
    144         ║100XXXXXXXXXXXXX║
    145         ║  │└─────┬─────┘║
    146         ║  │Address >> 1 ║
    147         ║  └── Value: 0  ║
    148         ╚════════════════╝
    149         0 <= Address <= 0x3FFE (16382)
    150 
    151         ╔ U16-Encoded 1 ═╗
    152         ║101XXXXXXXXXXXXX║
    153         ║  │└─────┬─────┘║
    154         ║  │Address >> 1 ║
    155         ║  └── Value: 1  ║
    156         ╚════════════════╝
    157         0 <= Address <= 0x3FFE (16382) */
    158 
    159 /**
    160  * Storage area for the wear-leveling cache.
    161  */
    162 static struct __attribute__((__aligned__(BACKING_STORE_WRITE_SIZE))) {
    163     __attribute__((__aligned__(BACKING_STORE_WRITE_SIZE))) uint8_t cache[(WEAR_LEVELING_LOGICAL_SIZE)];
    164     uint32_t                                                       write_address;
    165     bool                                                           unlocked;
    166 } wear_leveling;
    167 
    168 /**
    169  * Locking helper: status
    170  */
    171 typedef enum backing_store_lock_status_t { STATUS_FAILURE = 0, STATUS_SUCCESS, STATUS_UNCHANGED } backing_store_lock_status_t;
    172 
    173 /**
    174  * Locking helper: unlock
    175  */
    176 static inline backing_store_lock_status_t wear_leveling_unlock(void) {
    177     if (wear_leveling.unlocked) {
    178         return STATUS_UNCHANGED;
    179     }
    180     if (!backing_store_unlock()) {
    181         return STATUS_FAILURE;
    182     }
    183     wear_leveling.unlocked = true;
    184     return STATUS_SUCCESS;
    185 }
    186 
    187 /**
    188  * Locking helper: lock
    189  */
    190 static inline backing_store_lock_status_t wear_leveling_lock(void) {
    191     if (!wear_leveling.unlocked) {
    192         return STATUS_UNCHANGED;
    193     }
    194     if (!backing_store_lock()) {
    195         return STATUS_FAILURE;
    196     }
    197     wear_leveling.unlocked = false;
    198     return STATUS_SUCCESS;
    199 }
    200 
    201 /**
    202  * Resets the cache, ensuring the write address is correctly initialised.
    203  */
    204 static void wear_leveling_clear_cache(void) {
    205     memset(wear_leveling.cache, 0, (WEAR_LEVELING_LOGICAL_SIZE));
    206     wear_leveling.write_address = (WEAR_LEVELING_LOGICAL_SIZE) + 8; // +8 is due to the FNV1a_64 of the consolidated buffer
    207 }
    208 
    209 /**
    210  * Reads the consolidated data from the backing store into the cache.
    211  * Does not consider the write log.
    212  */
    213 static wear_leveling_status_t wear_leveling_read_consolidated(void) {
    214     wl_dprintf("Reading consolidated data\n");
    215 
    216     wear_leveling_status_t status = WEAR_LEVELING_SUCCESS;
    217     if (!backing_store_read_bulk(0, (backing_store_int_t *)wear_leveling.cache, sizeof(wear_leveling.cache) / sizeof(backing_store_int_t))) {
    218         wl_dprintf("Failed to read from backing store\n");
    219         status = WEAR_LEVELING_FAILED;
    220     }
    221 
    222     // Verify the FNV1a_64 result
    223     if (status != WEAR_LEVELING_FAILED) {
    224         uint64_t          expected = fnv_64a_buf(wear_leveling.cache, (WEAR_LEVELING_LOGICAL_SIZE), FNV1A_64_INIT);
    225         write_log_entry_t entry;
    226         wl_dprintf("Reading checksum\n");
    227 #if BACKING_STORE_WRITE_SIZE == 2
    228         backing_store_read_bulk((WEAR_LEVELING_LOGICAL_SIZE), entry.raw16, 4);
    229 #elif BACKING_STORE_WRITE_SIZE == 4
    230         backing_store_read_bulk((WEAR_LEVELING_LOGICAL_SIZE), entry.raw32, 2);
    231 #elif BACKING_STORE_WRITE_SIZE == 8
    232         backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 0, &entry.raw64);
    233 #endif
    234         // If we have a mismatch, clear the cache but do not flag a failure,
    235         // which will cater for the completely clean MCU case.
    236         if (entry.raw64 == expected) {
    237             wl_dprintf("Checksum matches, consolidated data is correct\n");
    238         } else {
    239             wl_dprintf("Checksum mismatch, clearing cache\n");
    240             wear_leveling_clear_cache();
    241         }
    242     }
    243 
    244     // If we failed for any reason, then clear the cache
    245     if (status == WEAR_LEVELING_FAILED) {
    246         wear_leveling_clear_cache();
    247     }
    248 
    249     return status;
    250 }
    251 
    252 /**
    253  * Writes the current cache to consolidated data at the beginning of the backing store.
    254  * Does not clear the write log.
    255  * Pre-condition: this is just after an erase, so we can write directly without reading.
    256  */
    257 static wear_leveling_status_t wear_leveling_write_consolidated(void) {
    258     wl_dprintf("Writing consolidated data\n");
    259 
    260     backing_store_lock_status_t lock_status = wear_leveling_unlock();
    261     wear_leveling_status_t      status      = WEAR_LEVELING_CONSOLIDATED;
    262     if (!backing_store_write_bulk(0, (backing_store_int_t *)wear_leveling.cache, sizeof(wear_leveling.cache) / sizeof(backing_store_int_t))) {
    263         wl_dprintf("Failed to write to backing store\n");
    264         status = WEAR_LEVELING_FAILED;
    265     }
    266 
    267     if (status != WEAR_LEVELING_FAILED) {
    268         // Write out the FNV1a_64 result of the consolidated data
    269         write_log_entry_t entry;
    270         entry.raw64 = fnv_64a_buf(wear_leveling.cache, (WEAR_LEVELING_LOGICAL_SIZE), FNV1A_64_INIT);
    271         wl_dprintf("Writing checksum\n");
    272         do {
    273 #if BACKING_STORE_WRITE_SIZE == 2
    274             if (!backing_store_write_bulk((WEAR_LEVELING_LOGICAL_SIZE), entry.raw16, 4)) {
    275                 status = WEAR_LEVELING_FAILED;
    276                 break;
    277             }
    278 #elif BACKING_STORE_WRITE_SIZE == 4
    279             if (!backing_store_write_bulk((WEAR_LEVELING_LOGICAL_SIZE), entry.raw32, 2)) {
    280                 status = WEAR_LEVELING_FAILED;
    281                 break;
    282             }
    283 #elif BACKING_STORE_WRITE_SIZE == 8
    284             if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE), entry.raw64)) {
    285                 status = WEAR_LEVELING_FAILED;
    286                 break;
    287             }
    288 #endif
    289         } while (0);
    290     }
    291 
    292     if (lock_status == STATUS_SUCCESS) {
    293         wear_leveling_lock();
    294     }
    295     return status;
    296 }
    297 
    298 /**
    299  * Forces a write of the current cache.
    300  * Erases the backing store, including the write log.
    301  * During this operation, there is the potential for data loss if a power loss occurs.
    302  */
    303 static wear_leveling_status_t wear_leveling_consolidate_force(void) {
    304     wl_dprintf("Erasing backing store\n");
    305 
    306     // Erase the backing store. Expectation is that any un-written values that are read back after this call come back as zero.
    307     bool ok = backing_store_erase();
    308     if (!ok) {
    309         wl_dprintf("Failed to erase backing store\n");
    310         return WEAR_LEVELING_FAILED;
    311     }
    312 
    313     // Write the cache to the first section of the backing store.
    314     wear_leveling_status_t status = wear_leveling_write_consolidated();
    315     if (status == WEAR_LEVELING_FAILED) {
    316         wl_dprintf("Failed to write consolidated data\n");
    317     }
    318 
    319     // Next write of the log occurs after the consolidated values at the start of the backing store.
    320     wear_leveling.write_address = (WEAR_LEVELING_LOGICAL_SIZE) + 8; // +8 due to the FNV1a_64 of the consolidated area
    321 
    322     return status;
    323 }
    324 
    325 /**
    326  * Potential write of the current cache to the backing store.
    327  * Skipped if the current write log position is not at the end of the backing store.
    328  * During this operation, there is the potential for data loss if a power loss occurs.
    329  *
    330  * @return true if consolidation occurred
    331  */
    332 static wear_leveling_status_t wear_leveling_consolidate_if_needed(void) {
    333     if (wear_leveling.write_address >= (WEAR_LEVELING_BACKING_SIZE)) {
    334         return wear_leveling_consolidate_force();
    335     }
    336 
    337     return WEAR_LEVELING_SUCCESS;
    338 }
    339 
    340 /**
    341  * Appends the supplied fixed-width entry to the write log, optionally consolidating if the log is full.
    342  *
    343  * @return true if consolidation occurred
    344  */
    345 static wear_leveling_status_t wear_leveling_append_raw(backing_store_int_t value) {
    346     bool ok = backing_store_write(wear_leveling.write_address, value);
    347     if (!ok) {
    348         wl_dprintf("Failed to write to backing store\n");
    349         return WEAR_LEVELING_FAILED;
    350     }
    351     wear_leveling.write_address += (BACKING_STORE_WRITE_SIZE);
    352     return wear_leveling_consolidate_if_needed();
    353 }
    354 
    355 /**
    356  * Handles writing multi_byte-encoded data to the backing store.
    357  *
    358  * @return true if consolidation occurred
    359  */
    360 static wear_leveling_status_t wear_leveling_write_raw_multibyte(uint32_t address, const void *value, size_t length) {
    361     const uint8_t    *p   = value;
    362     write_log_entry_t log = LOG_ENTRY_MAKE_MULTIBYTE(address, length);
    363     for (size_t i = 0; i < length; ++i) {
    364         log.raw8[3 + i] = p[i];
    365     }
    366 
    367     // Write to the backing store. See the multi-byte log format in the documentation header at the top of the file.
    368     wear_leveling_status_t status;
    369 #if BACKING_STORE_WRITE_SIZE == 2
    370     status = wear_leveling_append_raw(log.raw16[0]);
    371     if (status != WEAR_LEVELING_SUCCESS) {
    372         return status;
    373     }
    374 
    375     status = wear_leveling_append_raw(log.raw16[1]);
    376     if (status != WEAR_LEVELING_SUCCESS) {
    377         return status;
    378     }
    379 
    380     if (length > 1) {
    381         status = wear_leveling_append_raw(log.raw16[2]);
    382         if (status != WEAR_LEVELING_SUCCESS) {
    383             return status;
    384         }
    385     }
    386 
    387     if (length > 3) {
    388         status = wear_leveling_append_raw(log.raw16[3]);
    389         if (status != WEAR_LEVELING_SUCCESS) {
    390             return status;
    391         }
    392     }
    393 #elif BACKING_STORE_WRITE_SIZE == 4
    394     status = wear_leveling_append_raw(log.raw32[0]);
    395     if (status != WEAR_LEVELING_SUCCESS) {
    396         return status;
    397     }
    398 
    399     if (length > 1) {
    400         status = wear_leveling_append_raw(log.raw32[1]);
    401         if (status != WEAR_LEVELING_SUCCESS) {
    402             return status;
    403         }
    404     }
    405 #elif BACKING_STORE_WRITE_SIZE == 8
    406     status = wear_leveling_append_raw(log.raw64);
    407     if (status != WEAR_LEVELING_SUCCESS) {
    408         return status;
    409     }
    410 #endif
    411     return status;
    412 }
    413 
    414 /**
    415  * Handles the actual writing of logical data into the write log section of the backing store.
    416  */
    417 static wear_leveling_status_t wear_leveling_write_raw(uint32_t address, const void *value, size_t length) {
    418     const uint8_t         *p         = value;
    419     size_t                 remaining = length;
    420     wear_leveling_status_t status    = WEAR_LEVELING_SUCCESS;
    421     while (remaining > 0) {
    422 #if BACKING_STORE_WRITE_SIZE == 2
    423         // Small-write optimizations - uint16_t, 0 or 1, address is even, address <16384:
    424         if (remaining >= 2 && address % 2 == 0 && address < 16384) {
    425             const uint16_t v = ((uint16_t)p[1]) << 8 | p[0]; // don't just dereference a uint16_t here -- if unaligned it generates faults on some MCUs
    426             if (v == 0 || v == 1) {
    427                 const write_log_entry_t log = LOG_ENTRY_MAKE_WORD_01(address, v);
    428                 status                      = wear_leveling_append_raw(log.raw16[0]);
    429                 if (status != WEAR_LEVELING_SUCCESS) {
    430                     // If consolidation occurred, then the cache has already been written to the consolidated area. No need to continue.
    431                     // If a failure occurred, pass it on.
    432                     return status;
    433                 }
    434 
    435                 remaining -= 2;
    436                 address += 2;
    437                 p += 2;
    438                 continue;
    439             }
    440         }
    441 
    442         // Small-write optimizations - address<64:
    443         if (address < 64) {
    444             const write_log_entry_t log = LOG_ENTRY_MAKE_OPTIMIZED_64(address, *p);
    445             status                      = wear_leveling_append_raw(log.raw16[0]);
    446             if (status != WEAR_LEVELING_SUCCESS) {
    447                 // If consolidation occurred, then the cache has already been written to the consolidated area. No need to continue.
    448                 // If a failure occurred, pass it on.
    449                 return status;
    450             }
    451 
    452             remaining--;
    453             address++;
    454             p++;
    455             continue;
    456         }
    457 #endif // BACKING_STORE_WRITE_SIZE == 2
    458         const size_t this_length = remaining >= LOG_ENTRY_MULTIBYTE_MAX_BYTES ? LOG_ENTRY_MULTIBYTE_MAX_BYTES : remaining;
    459         status                   = wear_leveling_write_raw_multibyte(address, p, this_length);
    460         if (status != WEAR_LEVELING_SUCCESS) {
    461             // If consolidation occurred, then the cache has already been written to the consolidated area. No need to continue.
    462             // If a failure occurred, pass it on.
    463             return status;
    464         }
    465         remaining -= this_length;
    466         address += (uint32_t)this_length;
    467         p += this_length;
    468     }
    469 
    470     return status;
    471 }
    472 
    473 /**
    474  * "Replays" the write log from the backing store, updating the local cache with updated values.
    475  */
    476 static wear_leveling_status_t wear_leveling_playback_log(void) {
    477     wl_dprintf("Playback write log\n");
    478 
    479     wear_leveling_status_t status          = WEAR_LEVELING_SUCCESS;
    480     bool                   cancel_playback = false;
    481     uint32_t               address         = (WEAR_LEVELING_LOGICAL_SIZE) + 8; // +8 due to the FNV1a_64 of the consolidated area
    482     while (!cancel_playback && address < (WEAR_LEVELING_BACKING_SIZE)) {
    483         backing_store_int_t value;
    484         bool                ok = backing_store_read(address, &value);
    485         if (!ok) {
    486             wl_dprintf("Failed to load from backing store, skipping playback of write log\n");
    487             cancel_playback = true;
    488             status          = WEAR_LEVELING_FAILED;
    489             break;
    490         }
    491         if (value == 0) {
    492             wl_dprintf("Found empty slot, no more log entries\n");
    493             cancel_playback = true;
    494             break;
    495         }
    496 
    497         // If we got a nonzero value, then we need to increment the address to ensure next write occurs at next location
    498         address += (BACKING_STORE_WRITE_SIZE);
    499 
    500         // Read from the write log
    501         write_log_entry_t log;
    502 #if BACKING_STORE_WRITE_SIZE == 2
    503         log.raw16[0] = value;
    504 #elif BACKING_STORE_WRITE_SIZE == 4
    505         log.raw32[0] = value;
    506 #elif BACKING_STORE_WRITE_SIZE == 8
    507         log.raw64 = value;
    508 #endif
    509 
    510         switch (LOG_ENTRY_GET_TYPE(log)) {
    511             case LOG_ENTRY_TYPE_MULTIBYTE: {
    512 #if BACKING_STORE_WRITE_SIZE == 2
    513                 ok = backing_store_read(address, &log.raw16[1]);
    514                 if (!ok) {
    515                     wl_dprintf("Failed to load from backing store, skipping playback of write log\n");
    516                     cancel_playback = true;
    517                     status          = WEAR_LEVELING_FAILED;
    518                     break;
    519                 }
    520                 address += (BACKING_STORE_WRITE_SIZE);
    521 #endif // BACKING_STORE_WRITE_SIZE == 2
    522                 const uint32_t a = LOG_ENTRY_MULTIBYTE_GET_ADDRESS(log);
    523                 const uint8_t  l = LOG_ENTRY_MULTIBYTE_GET_LENGTH(log);
    524 
    525                 if (a + l > (WEAR_LEVELING_LOGICAL_SIZE)) {
    526                     cancel_playback = true;
    527                     status          = WEAR_LEVELING_FAILED;
    528                     break;
    529                 }
    530 
    531 #if BACKING_STORE_WRITE_SIZE == 2
    532                 if (l > 1) {
    533                     ok = backing_store_read(address, &log.raw16[2]);
    534                     if (!ok) {
    535                         wl_dprintf("Failed to load from backing store, skipping playback of write log\n");
    536                         cancel_playback = true;
    537                         status          = WEAR_LEVELING_FAILED;
    538                         break;
    539                     }
    540                     address += (BACKING_STORE_WRITE_SIZE);
    541                 }
    542                 if (l > 3) {
    543                     ok = backing_store_read(address, &log.raw16[3]);
    544                     if (!ok) {
    545                         wl_dprintf("Failed to load from backing store, skipping playback of write log\n");
    546                         cancel_playback = true;
    547                         status          = WEAR_LEVELING_FAILED;
    548                         break;
    549                     }
    550                     address += (BACKING_STORE_WRITE_SIZE);
    551                 }
    552 #elif BACKING_STORE_WRITE_SIZE == 4
    553                 if (l > 1) {
    554                     ok = backing_store_read(address, &log.raw32[1]);
    555                     if (!ok) {
    556                         wl_dprintf("Failed to load from backing store, skipping playback of write log\n");
    557                         cancel_playback = true;
    558                         status          = WEAR_LEVELING_FAILED;
    559                         break;
    560                     }
    561                     address += (BACKING_STORE_WRITE_SIZE);
    562                 }
    563 #endif
    564 
    565                 memcpy(&wear_leveling.cache[a], &log.raw8[3], l);
    566             } break;
    567 #if BACKING_STORE_WRITE_SIZE == 2
    568             case LOG_ENTRY_TYPE_OPTIMIZED_64: {
    569                 const uint32_t a = LOG_ENTRY_OPTIMIZED_64_GET_ADDRESS(log);
    570                 const uint8_t  v = LOG_ENTRY_OPTIMIZED_64_GET_VALUE(log);
    571 
    572                 if (a >= (WEAR_LEVELING_LOGICAL_SIZE)) {
    573                     cancel_playback = true;
    574                     status          = WEAR_LEVELING_FAILED;
    575                     break;
    576                 }
    577 
    578                 wear_leveling.cache[a] = v;
    579             } break;
    580             case LOG_ENTRY_TYPE_WORD_01: {
    581                 const uint32_t a = LOG_ENTRY_WORD_01_GET_ADDRESS(log);
    582                 const uint8_t  v = LOG_ENTRY_WORD_01_GET_VALUE(log);
    583 
    584                 if (a + 1 >= (WEAR_LEVELING_LOGICAL_SIZE)) {
    585                     cancel_playback = true;
    586                     status          = WEAR_LEVELING_FAILED;
    587                     break;
    588                 }
    589 
    590                 wear_leveling.cache[a + 0] = v;
    591                 wear_leveling.cache[a + 1] = 0;
    592             } break;
    593 #endif // BACKING_STORE_WRITE_SIZE == 2
    594             default: {
    595                 cancel_playback = true;
    596                 status          = WEAR_LEVELING_FAILED;
    597             } break;
    598         }
    599     }
    600 
    601     // We've reached the end of the log, so we're at the new write location
    602     wear_leveling.write_address = address;
    603 
    604     if (status == WEAR_LEVELING_FAILED) {
    605         // If we had a failure during readback, assume we're corrupted -- force a consolidation with the data we already have
    606         status = wear_leveling_consolidate_force();
    607     } else {
    608         // Consolidate the cache + write log if required
    609         status = wear_leveling_consolidate_if_needed();
    610     }
    611 
    612     return status;
    613 }
    614 
    615 /**
    616  * Wear-leveling initialization
    617  */
    618 wear_leveling_status_t wear_leveling_init(void) {
    619     wl_dprintf("Init\n");
    620 
    621     // Reset the cache
    622     wear_leveling_clear_cache();
    623 
    624     // Initialise the backing store
    625     if (!backing_store_init()) {
    626         // If it failed, clear the cache and return with failure
    627         wear_leveling_clear_cache();
    628         return WEAR_LEVELING_FAILED;
    629     }
    630 
    631     // Read the previous consolidated values, then replay the existing write log so that the cache has the "live" values
    632     wear_leveling_status_t status = wear_leveling_read_consolidated();
    633     if (status == WEAR_LEVELING_FAILED) {
    634         // If it failed, clear the cache and return with failure
    635         wear_leveling_clear_cache();
    636         return status;
    637     }
    638 
    639     status = wear_leveling_playback_log();
    640     if (status == WEAR_LEVELING_FAILED) {
    641         // If it failed, clear the cache and return with failure
    642         wear_leveling_clear_cache();
    643         return status;
    644     }
    645 
    646     return status;
    647 }
    648 
    649 /**
    650  * Wear-leveling erase.
    651  * Post-condition: any reads from the backing store directly after an erase operation must come back as zero.
    652  */
    653 wear_leveling_status_t wear_leveling_erase(void) {
    654     wl_dprintf("Erase\n");
    655 
    656     // Unlock the backing store
    657     backing_store_lock_status_t lock_status = wear_leveling_unlock();
    658     if (lock_status == STATUS_FAILURE) {
    659         wear_leveling_lock();
    660         return WEAR_LEVELING_FAILED;
    661     }
    662 
    663     // Perform the erase
    664     bool ret = backing_store_erase();
    665     wear_leveling_clear_cache();
    666 
    667     // Lock the backing store if we acquired the lock successfully
    668     if (lock_status == STATUS_SUCCESS) {
    669         ret &= (wear_leveling_lock() != STATUS_FAILURE);
    670     }
    671 
    672     return ret ? WEAR_LEVELING_SUCCESS : WEAR_LEVELING_FAILED;
    673 }
    674 
    675 /**
    676  * Writes logical data into the backing store. Skips writes if there are no changes to values.
    677  */
    678 wear_leveling_status_t wear_leveling_write(const uint32_t address, const void *value, size_t length) {
    679     wl_assert(address + length <= (WEAR_LEVELING_LOGICAL_SIZE));
    680     if (address + length > (WEAR_LEVELING_LOGICAL_SIZE)) {
    681         return WEAR_LEVELING_FAILED;
    682     }
    683 
    684     wl_dprintf("Write ");
    685     wl_dump(address, value, length);
    686 
    687     // Skip write if there's no change compared to the current cached value
    688     if (memcmp(value, &wear_leveling.cache[address], length) == 0) {
    689         return true;
    690     }
    691 
    692     // Update the cache before writing to the backing store -- if we hit the end of the backing store during writes to the log then we'll force a consolidation in-line
    693     memcpy(&wear_leveling.cache[address], value, length);
    694 
    695     // Unlock the backing store
    696     backing_store_lock_status_t lock_status = wear_leveling_unlock();
    697     if (lock_status == STATUS_FAILURE) {
    698         wear_leveling_lock();
    699         return WEAR_LEVELING_FAILED;
    700     }
    701 
    702     // Perform the actual write
    703     wear_leveling_status_t status = wear_leveling_write_raw(address, value, length);
    704     switch (status) {
    705         case WEAR_LEVELING_CONSOLIDATED:
    706         case WEAR_LEVELING_FAILED:
    707             // If the write triggered consolidation, or the write failed, then nothing else needs to occur.
    708             break;
    709 
    710         case WEAR_LEVELING_SUCCESS:
    711             // Consolidate the cache + write log if required
    712             status = wear_leveling_consolidate_if_needed();
    713             break;
    714 
    715         default:
    716             // Unsure how we'd get here...
    717             status = WEAR_LEVELING_FAILED;
    718             break;
    719     }
    720 
    721     if (lock_status == STATUS_SUCCESS) {
    722         if (wear_leveling_lock() == STATUS_FAILURE) {
    723             status = WEAR_LEVELING_FAILED;
    724         }
    725     }
    726 
    727     return status;
    728 }
    729 
    730 /**
    731  * Reads logical data from the cache.
    732  */
    733 wear_leveling_status_t wear_leveling_read(const uint32_t address, void *value, size_t length) {
    734     wl_assert(address + length <= (WEAR_LEVELING_LOGICAL_SIZE));
    735     if (address + length > (WEAR_LEVELING_LOGICAL_SIZE)) {
    736         return WEAR_LEVELING_FAILED;
    737     }
    738 
    739     // Only need to copy from the cache
    740     memcpy(value, &wear_leveling.cache[address], length);
    741 
    742     wl_dprintf("Read  ");
    743     wl_dump(address, value, length);
    744     return WEAR_LEVELING_SUCCESS;
    745 }
    746 
    747 /**
    748  * Weak implementation of bulk read, drivers can implement more optimised implementations.
    749  */
    750 __attribute__((weak)) bool backing_store_read_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) {
    751     for (size_t i = 0; i < item_count; ++i) {
    752         if (!backing_store_read(address + (i * BACKING_STORE_WRITE_SIZE), &values[i])) {
    753             return false;
    754         }
    755     }
    756     return true;
    757 }
    758 
    759 /**
    760  * Weak implementation of bulk write, drivers can implement more optimised implementations.
    761  */
    762 __attribute__((weak)) bool backing_store_write_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) {
    763     for (size_t i = 0; i < item_count; ++i) {
    764         if (!backing_store_write(address + (i * BACKING_STORE_WRITE_SIZE), values[i])) {
    765             return false;
    766         }
    767     }
    768     return true;
    769 }