GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lx_nor_flash_block_reclaim.c Lines: 62 127 48.8 %
Date: 2026-03-06 18:45:40 Branches: 33 74 44.6 %

Line Branch Exec Source
1
/***************************************************************************
2
 * Copyright (c) 2024 Microsoft Corporation
3
 * Copyright (c) 2026-present Eclipse ThreadX contributors
4
 *
5
 * This program and the accompanying materials are made available under the
6
 * terms of the MIT License which is available at
7
 * https://opensource.org/licenses/MIT.
8
 *
9
 * SPDX-License-Identifier: MIT
10
 **************************************************************************/
11
12
13
/**************************************************************************/
14
/**************************************************************************/
15
/**                                                                       */
16
/** LevelX Component                                                      */
17
/**                                                                       */
18
/**   NOR Flash                                                           */
19
/**                                                                       */
20
/**************************************************************************/
21
/**************************************************************************/
22
23
#define LX_SOURCE_CODE
24
25
26
/* Disable ThreadX error checking.  */
27
28
#ifndef LX_DISABLE_ERROR_CHECKING
29
#define LX_DISABLE_ERROR_CHECKING
30
#endif
31
32
33
/* Include necessary system files.  */
34
35
#include "lx_api.h"
36
37
38
/**************************************************************************/
39
/*                                                                        */
40
/*  FUNCTION                                               RELEASE        */
41
/*                                                                        */
42
/*    _lx_nor_flash_block_reclaim                         PORTABLE C      */
43
/*                                                           6.3.0        */
44
/*  AUTHOR                                                                */
45
/*                                                                        */
46
/*    William E. Lamie, Microsoft Corporation                             */
47
/*                                                                        */
48
/*  DESCRIPTION                                                           */
49
/*                                                                        */
50
/*    This function reclaims one block from the NOR flash.                */
51
/*                                                                        */
52
/*  INPUT                                                                 */
53
/*                                                                        */
54
/*    nor_flash                             NOR flash instance            */
55
/*                                                                        */
56
/*  OUTPUT                                                                */
57
/*                                                                        */
58
/*    return status                                                       */
59
/*                                                                        */
60
/*  CALLS                                                                 */
61
/*                                                                        */
62
/*    _lx_nor_flash_driver_block_erase      Driver erase block            */
63
/*    _lx_nor_flash_driver_write            Driver flash sector write     */
64
/*    _lx_nor_flash_driver_read             Driver flash sector read      */
65
/*    _lx_nor_flash_next_block_to_erase_find                              */
66
/*                                          Find next block to erase      */
67
/*    _lx_nor_flash_physical_sector_allocate                              */
68
/*                                          Allocate new logical sector   */
69
/*    _lx_nor_flash_sector_mapping_cache_invalidate                       */
70
/*                                          Invalidate cache entry        */
71
/*    _lx_nor_flash_system_error            Internal system error handler */
72
/*                                                                        */
73
/*  CALLED BY                                                             */
74
/*                                                                        */
75
/*    Internal LevelX                                                     */
76
/*                                                                        */
77
/**************************************************************************/
78
1160
UINT  _lx_nor_flash_block_reclaim(LX_NOR_FLASH *nor_flash)
79
{
80
81
ULONG   *block_word_ptr;
82
ULONG   *list_word_ptr;
83
ULONG   list_word;
84
ULONG   i;
85
ULONG   erase_block;
86
ULONG   erase_count;
87
ULONG   temp_erase_count;
88
ULONG   erase_started_value;
89
ULONG   mapped_sectors;
90
ULONG   obsolete_sectors;
91
ULONG   free_sectors;
92
ULONG   logical_sector;
93
ULONG   *new_mapping_address;
94
ULONG   *new_sector_address;
95
ULONG   new_mapping_entry;
96
UINT    status;
97
98
99
    /* Determine the next block to erase.  */
100
1160
    _lx_nor_flash_next_block_to_erase_find(nor_flash, &erase_block, &erase_count, &mapped_sectors, &obsolete_sectors);
101
102
    /* Determine if the search pointer is set for this block.  */
103
1160
    if (nor_flash -> lx_nor_flash_free_block_search == erase_block)
104
    {
105
106
        /* Ensure the search block is not the block we are trying to free.  */
107
196
        nor_flash -> lx_nor_flash_free_block_search =  erase_block + 1;
108
109
        /* Check for wrap condition.  */
110
196
        if (nor_flash -> lx_nor_flash_free_block_search >= nor_flash -> lx_nor_flash_total_blocks)
111
22
            nor_flash -> lx_nor_flash_free_block_search =  0;
112
    }
113
114
    /* Setup the block word pointer to the first word of the search block.  */
115
1160
    block_word_ptr =  nor_flash -> lx_nor_flash_base_address + (nor_flash -> lx_nor_flash_words_per_block * erase_block);
116
117
    /* Determine if this block is completely obsolete.  */
118
1160
    if (obsolete_sectors == nor_flash -> lx_nor_flash_physical_sectors_per_block)
119
    {
120
121
        /* Write the erased started indication.  */
122
        erase_started_value =  LX_BLOCK_ERASE_STARTED;
123
        status =  _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_started_value, 1);
124
125
        /* Check for an error from flash driver. Drivers should never return an error..  */
126
        if (status)
127
        {
128
129
            /* Call system error handler.  */
130
            _lx_nor_flash_system_error(nor_flash, status);
131
132
            /* Return the error.  */
133
            return(status);
134
        }
135
136
        /* Erase the entire block.  */
137
        status =  _lx_nor_flash_driver_block_erase(nor_flash, erase_block, erase_count+1);
138
139
        /* Check for an error from flash driver. Drivers should never return an error..  */
140
        if (status)
141
        {
142
143
            /* Call system error handler.  */
144
            _lx_nor_flash_system_error(nor_flash, status);
145
146
            /* Return the error.  */
147
            return(status);
148
        }
149
150
        /* Determine if the erase count is at the minimum.  */
151
        if (erase_count == nor_flash -> lx_nor_flash_minimum_erase_count)
152
        {
153
154
            /* Yes, decrement the minimum erased block count.  */
155
            nor_flash -> lx_nor_flash_minimum_erased_blocks--;
156
        }
157
158
        /* Increment the erase count.  */
159
        erase_count++;
160
161
        /* Determine if the new erase count exceeds the maximum.  */
162
        if (erase_count > ((ULONG) LX_BLOCK_ERASE_COUNT_MAX))
163
        {
164
165
            /* Yes, erase count is in overflow. Stay at the maximum count.  */
166
            erase_count =  ((ULONG) LX_BLOCK_ERASE_COUNT_MAX);
167
        }
168
169
        /* Determine if we need to update the maximum erase count.  */
170
        if (erase_count > nor_flash -> lx_nor_flash_maximum_erase_count)
171
        {
172
173
            /* Yes, a new maximum is present.  */
174
            nor_flash -> lx_nor_flash_maximum_erase_count =  erase_count;
175
        }
176
177
        /* Setup the free bit map that corresponds to the free physical sectors in this
178
           block. Note that we only need to setup the portion of the free bit map that doesn't
179
           have sectors associated with it.  */
180
        status =  _lx_nor_flash_driver_write(nor_flash, block_word_ptr+(nor_flash -> lx_nor_flash_block_free_bit_map_offset + (nor_flash -> lx_nor_flash_block_bit_map_words - 1)),
181
                                                                        &(nor_flash -> lx_nor_flash_block_bit_map_mask), 1);
182
183
        /* Check for an error from flash driver. Drivers should never return an error..  */
184
        if (status)
185
        {
186
187
            /* Call system error handler.  */
188
            _lx_nor_flash_system_error(nor_flash, status);
189
190
            /* Return the error.  */
191
            return(status);
192
        }
193
194
        /* Write the initial erase count for the block with upper bit set.  */
195
        temp_erase_count =  (erase_count | LX_BLOCK_ERASED);
196
        status =  _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &temp_erase_count, 1);
197
198
        /* Check for an error from flash driver. Drivers should never return an error..  */
199
        if (status)
200
        {
201
202
            /* Call system error handler.  */
203
            _lx_nor_flash_system_error(nor_flash, status);
204
205
            /* Return the error.  */
206
            return(status);
207
        }
208
209
        /* Write the final initial erase count for the block.  */
210
        status =  _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_count, 1);
211
212
        /* Check for an error from flash driver. Drivers should never return an error..  */
213
        if (status)
214
        {
215
216
            /* Call system error handler.  */
217
            _lx_nor_flash_system_error(nor_flash, status);
218
219
            /* Return the error.  */
220
            return(status);
221
        }
222
223
        /* Update parameters of this flash.  */
224
        nor_flash -> lx_nor_flash_free_physical_sectors =      nor_flash -> lx_nor_flash_free_physical_sectors + obsolete_sectors;
225
        nor_flash -> lx_nor_flash_obsolete_physical_sectors =  nor_flash -> lx_nor_flash_obsolete_physical_sectors - obsolete_sectors;
226
#ifdef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
227
228
        /* Check if the block is cached by obsolete count cache.  */
229
        if (erase_block < nor_flash -> lx_nor_flash_extended_cache_obsolete_count_max_block)
230
        {
231
232
            /* Yes, clear the obsolete count for this block.  */
233
            nor_flash -> lx_nor_flash_extended_cache_obsolete_count[erase_block] =  0;
234
        }
235
#endif
236
    }
237
    else
238
    {
239
240
        /* Calculate the number of free sectors in this block.  */
241
1160
        free_sectors =  nor_flash -> lx_nor_flash_physical_sectors_per_block - (obsolete_sectors + mapped_sectors);
242
243
        /* Determine if there are enough free sectors outside of this block to reclaim this block.  */
244
1160
        if (mapped_sectors <= (nor_flash -> lx_nor_flash_free_physical_sectors - free_sectors))
245
        {
246
247
            /* Setup a pointer to the mapped list.  */
248
1160
            list_word_ptr =  block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset;
249
250
            /* Now search through the list to find mapped sectors to move.  */
251
17106
            for (i = 0; i < nor_flash -> lx_nor_flash_physical_sectors_per_block; i++)
252
            {
253
254
                /* Pickup the mapped sector list entry.  */
255
#ifdef LX_DIRECT_READ
256
257
                /* Read the word directly.  */
258
                list_word =  *(list_word_ptr);
259
#else
260
17106
                status =  _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_word, 1);
261
262
                /* Check for an error from flash driver. Drivers should never return an error..  */
263
17106
                if (status)
264
                {
265
266
                    /* Call system error handler.  */
267
                    _lx_nor_flash_system_error(nor_flash, status);
268
269
                    /* Return the error.  */
270
                    return(status);
271
                }
272
#endif
273
274
                /* Determine if the entry hasn't been used.  */
275
17106
                if (list_word == LX_NOR_PHYSICAL_SECTOR_FREE)
276
                {
277
278
                    /* Since allocations are done sequentially in the block, we know nothing
279
                       else exists after this point.  */
280
                    break;
281
                }
282
283
                /* Is this entry mapped?  */
284
17106
                if (list_word & LX_NOR_PHYSICAL_SECTOR_VALID)
285
                {
286
287
                    /* Pickup the logical sector associated with this mapped physical sector.  */
288
15118
                    logical_sector =  list_word & LX_NOR_LOGICAL_SECTOR_MASK;
289
290
                    /* Invalidate the old sector mapping cache entry.  */
291
15118
                    _lx_nor_flash_sector_mapping_cache_invalidate(nor_flash, logical_sector);
292
293
                    /* Allocate a new physical sector for this write.  */
294
15118
                    _lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &new_mapping_address, &new_sector_address);
295
296
                    /* Check to see if the new sector is also in the erase block.  */
297

15118
                    if ((new_sector_address >= block_word_ptr) && (new_sector_address < (block_word_ptr + nor_flash -> lx_nor_flash_words_per_block)))
298
                    {
299
300
                        /* Yes, the new sector was found in the block to be erased. Simply move the search pointer
301
                           to the block after the erase block and search for another sector from there.  */
302
                        nor_flash -> lx_nor_flash_free_block_search =  erase_block + 1;
303
304
                        /* Check for wrap condition.  */
305
                        if (nor_flash -> lx_nor_flash_free_block_search >= nor_flash -> lx_nor_flash_total_blocks)
306
                            nor_flash -> lx_nor_flash_free_block_search =  0;
307
308
                        /* Allocate a new physical sector for this write.  */
309
                        _lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &new_mapping_address, &new_sector_address);
310
311
                        /* Check again for the new sector inside of the block to erase. This should be impossible, since
312
                           we check previously if there are enough free sectors outside of this block needed to reclaim
313
                           this block.  */
314
                        if ((new_sector_address >= block_word_ptr) && (new_sector_address < (block_word_ptr + LX_NOR_SECTOR_SIZE)))
315
                        {
316
317
                            /* System error, a new sector is not available outside of the erase block.
318
                               Clear the new sector so we fall through to the error handling. */
319
                            new_mapping_address =  LX_NULL;
320
                        }
321
                    }
322
323
                    /* Determine if the new sector allocation was successful.  */
324
15118
                    if (new_mapping_address)
325
                    {
326
327
                        /* Yes, we were able to allocate a new physical sector.  */
328
329
#ifdef LX_DIRECT_READ
330
                        /* First, write the sector data to the new physical sector.  */
331
                        status =  _lx_nor_flash_driver_write(nor_flash, new_sector_address, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset) +
332
                                                                        (i * LX_NOR_SECTOR_SIZE), LX_NOR_SECTOR_SIZE);
333
334
                        /* Check for an error from flash driver. Drivers should never return an error..  */
335
                        if (status)
336
                        {
337
338
                            /* Call system error handler.  */
339
                            _lx_nor_flash_system_error(nor_flash, status);
340
341
                            /* Return the error.  */
342
                            return(status);
343
                        }
344
#else
345
346
                        /* First, read the sector data into the internal memory of the NOR flash instance. This internal memory
347
                           is supplied by the underlying driver during initialization.  */
348
15118
                        status =  _lx_nor_flash_driver_read(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset) +
349
                                                                                       (i * LX_NOR_SECTOR_SIZE), nor_flash -> lx_nor_flash_sector_buffer,
350
                                                                                       LX_NOR_SECTOR_SIZE);
351
352
                        /* Check for an error from flash driver. Drivers should never return an error..  */
353
15118
                        if (status)
354
                        {
355
356
                            /* Call system error handler.  */
357
                            _lx_nor_flash_system_error(nor_flash, status);
358
359
                            /* Return the error.  */
360
                            return(status);
361
                        }
362
363
                        /* Next, write the sector data from the internal buffer to the new physical sector.  */
364
15118
                        status =  _lx_nor_flash_driver_write(nor_flash, new_sector_address, nor_flash -> lx_nor_flash_sector_buffer, LX_NOR_SECTOR_SIZE);
365
366
                        /* Check for an error from flash driver. Drivers should never return an error..  */
367
15118
                        if (status)
368
                        {
369
370
                            /* Call system error handler.  */
371
                            _lx_nor_flash_system_error(nor_flash, status);
372
373
                            /* Return the error.  */
374
                            return(status);
375
                        }
376
#endif
377
378
                        /* Now deprecate the old sector mapping.  */
379
380
                        /* Clear bit 30, which indicates this sector is superceded.  */
381
15118
                        list_word =  list_word & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED);
382
383
                        /* Write the value back to the flash to clear bit 30.  */
384
15118
                        status =  _lx_nor_flash_driver_write(nor_flash, list_word_ptr, &list_word, 1);
385
386
                        /* Check for an error from flash driver. Drivers should never return an error..  */
387
15118
                        if (status)
388
                        {
389
390
                            /* Call system error handler.  */
391
                            _lx_nor_flash_system_error(nor_flash, status);
392
393
                            /* Return the error.  */
394
                            return(status);
395
                        }
396
397
                        /* Now build the new mapping entry - with the not valid bit set initially.  */
398
15118
                        new_mapping_entry =  ((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID) | ((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED) | (ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID | logical_sector;
399
400
                        /* Write out the new mapping entry.  */
401
15118
                        status =  _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 1);
402
403
                        /* Check for an error from flash driver. Drivers should never return an error..  */
404
15118
                        if (status)
405
                        {
406
407
                            /* Call system error handler.  */
408
                            _lx_nor_flash_system_error(nor_flash, status);
409
410
                            /* Return the error.  */
411
                            return(status);
412
                        }
413
414
                        /* Now clear the not valid bit to make this sector mapping valid.  This is done because the writing of the extra bytes itself can
415
                           be interrupted and we need to make sure this can be detected when the flash is opened again.  */
416
15118
                        new_mapping_entry =  new_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID);
417
418
                        /* Clear the not valid bit.  */
419
15118
                        status =  _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 1);
420
421
                        /* Check for an error from flash driver. Drivers should never return an error..  */
422
15118
                        if (status)
423
                        {
424
425
                            /* Call system error handler.  */
426
                            _lx_nor_flash_system_error(nor_flash, status);
427
428
                            /* Return the error.  */
429
                            return(status);
430
                        }
431
432
                        /* Now clear bit 31, which indicates this sector is now obsoleted.  */
433
15118
                        list_word =  list_word & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID);
434
435
                        /* Write the value back to the flash to clear bit 31.  */
436
15118
                        status =  _lx_nor_flash_driver_write(nor_flash, list_word_ptr, &list_word, 1);
437
438
                        /* Check for an error from flash driver. Drivers should never return an error..  */
439
15118
                        if (status)
440
                        {
441
442
                            /* Call system error handler.  */
443
                            _lx_nor_flash_system_error(nor_flash, status);
444
445
                            /* Return the error.  */
446
                            return(status);
447
                        }
448
                    }
449
                    else
450
                    {
451
452
                        /* Call system error handler - the allocation should always succeed at this point.  */
453
                        _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_ALLOCATION_FAILED);
454
455
                        /* Return the error.  */
456
                        return(status);
457
                    }
458
459
                    /* Decrement the number of mapped sectors.  */
460
15118
                    mapped_sectors--;
461
462
                    /* Determine if we are done.  */
463
15118
                    if (mapped_sectors == 0)
464
1160
                        break;
465
               }
466
467
               /* Move the list pointer ahead.  */
468
15946
               list_word_ptr++;
469
            }
470
471
            /* Write the erased started indication.  */
472
1160
            erase_started_value =  LX_BLOCK_ERASE_STARTED;
473
1160
            status =  _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_started_value, 1);
474
475
            /* Check for an error from flash driver. Drivers should never return an error..  */
476
1160
            if (status)
477
            {
478
479
                /* Call system error handler.  */
480
                _lx_nor_flash_system_error(nor_flash, status);
481
482
                /* Return the error.  */
483
                return(status);
484
            }
485
486
            /* Erase the entire block.  */
487
1160
            status =  _lx_nor_flash_driver_block_erase(nor_flash, erase_block, erase_count+1);
488
489
            /* Check for an error from flash driver. Drivers should never return an error..  */
490
1160
            if (status)
491
            {
492
493
                /* Call system error handler.  */
494
                _lx_nor_flash_system_error(nor_flash, status);
495
496
                /* Return the error.  */
497
                return(status);
498
            }
499
500
            /* Determine if the erase count is at the minimum.  */
501
1160
            if (erase_count == nor_flash -> lx_nor_flash_minimum_erase_count)
502
            {
503
504
                /* Yes, decrement the minimum erased block count.  */
505
582
                nor_flash -> lx_nor_flash_minimum_erased_blocks--;
506
            }
507
508
            /* Increment the erase count.  */
509
1160
            erase_count++;
510
511
            /* Determine if the new erase count exceeds the maximum.  */
512
1160
            if (erase_count > ((ULONG) LX_BLOCK_ERASE_COUNT_MAX))
513
            {
514
515
                /* Yes, erase count is in overflow. Stay at the maximum count.  */
516
                erase_count =  ((ULONG) LX_BLOCK_ERASE_COUNT_MAX);
517
            }
518
519
            /* Determine if we need to update the maximum erase count.  */
520
1160
            if (erase_count > nor_flash -> lx_nor_flash_maximum_erase_count)
521
            {
522
523
                /* Yes, a new maximum is present.  */
524
150
                nor_flash -> lx_nor_flash_maximum_erase_count =  erase_count;
525
            }
526
527
            /* Setup the free bit map that corresponds to the free physical sectors in this
528
               block. Note that we only need to setup the portion of the free bit map that doesn't
529
               have sectors associated with it.  */
530
1160
            status =  _lx_nor_flash_driver_write(nor_flash, block_word_ptr+(nor_flash -> lx_nor_flash_block_free_bit_map_offset + (nor_flash -> lx_nor_flash_block_bit_map_words - 1)) ,
531
                                                                             &(nor_flash -> lx_nor_flash_block_bit_map_mask), 1);
532
533
            /* Check for an error from flash driver. Drivers should never return an error..  */
534
1160
            if (status)
535
            {
536
537
                /* Call system error handler.  */
538
                _lx_nor_flash_system_error(nor_flash, status);
539
540
                /* Return the error.  */
541
                return(status);
542
            }
543
544
            /* Write the initial erase count for the block with the upper bit set.  */
545
1160
            temp_erase_count =  (erase_count | LX_BLOCK_ERASED);
546
1160
            status =  _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &temp_erase_count, 1);
547
548
            /* Check for an error from flash driver. Drivers should never return an error..  */
549
1160
            if (status)
550
            {
551
552
                /* Call system error handler.  */
553
                _lx_nor_flash_system_error(nor_flash, status);
554
555
                /* Return the error.  */
556
                return(status);
557
            }
558
559
            /* Write the final initial erase count for the block.  */
560
1160
            status =  _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_count, 1);
561
562
            /* Check for an error from flash driver. Drivers should never return an error..  */
563
1160
            if (status)
564
            {
565
566
                /* Call system error handler.  */
567
                _lx_nor_flash_system_error(nor_flash, status);
568
569
                /* Return the error.  */
570
                return(status);
571
            }
572
573
            /* Update parameters of this flash.  */
574
1160
            nor_flash -> lx_nor_flash_free_physical_sectors =      nor_flash -> lx_nor_flash_free_physical_sectors + obsolete_sectors;
575
1160
            nor_flash -> lx_nor_flash_obsolete_physical_sectors =  nor_flash -> lx_nor_flash_obsolete_physical_sectors - obsolete_sectors;
576
#ifdef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
577
578
            /* Check if the block is cached by obsolete count cache.  */
579
            if (erase_block < nor_flash -> lx_nor_flash_extended_cache_obsolete_count_max_block)
580
            {
581
582
                /* Yes, clear the obsolete count for this block.  */
583
                nor_flash -> lx_nor_flash_extended_cache_obsolete_count[erase_block] =  0;
584
            }
585
#endif
586
        }
587
    }
588
589
    /* Return status.  */
590
1160
    return(LX_SUCCESS);
591
}
592