1 /***************************************************************************
2  * Copyright (c) 2024 Microsoft Corporation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the MIT License which is available at
6  * https://opensource.org/licenses/MIT.
7  *
8  * SPDX-License-Identifier: MIT
9  **************************************************************************/
10 
11 
12 /**************************************************************************/
13 /**************************************************************************/
14 /**                                                                       */
15 /** LevelX Component                                                      */
16 /**                                                                       */
17 /**   NOR Flash                                                           */
18 /**                                                                       */
19 /**************************************************************************/
20 /**************************************************************************/
21 
22 #define LX_SOURCE_CODE
23 
24 
25 /* Disable ThreadX error checking.  */
26 
27 #ifndef LX_DISABLE_ERROR_CHECKING
28 #define LX_DISABLE_ERROR_CHECKING
29 #endif
30 
31 
32 /* Include necessary system files.  */
33 
34 #include "lx_api.h"
35 
36 
37 /**************************************************************************/
38 /*                                                                        */
39 /*  FUNCTION                                               RELEASE        */
40 /*                                                                        */
41 /*    _lx_nor_flash_logical_sector_find                   PORTABLE C      */
42 /*                                                           6.3.0        */
43 /*  AUTHOR                                                                */
44 /*                                                                        */
45 /*    William E. Lamie, Microsoft Corporation                             */
46 /*                                                                        */
47 /*  DESCRIPTION                                                           */
48 /*                                                                        */
49 /*    This function attempts to find the specified logical sector in      */
50 /*    the NOR flash.                                                      */
51 /*                                                                        */
52 /*  INPUT                                                                 */
53 /*                                                                        */
54 /*    nor_flash                             NOR flash instance            */
55 /*    logical_sector                        Logical sector number         */
56 /*    superceded_check                      Check for sector being        */
57 /*                                            superceded (can happen if   */
58 /*                                            on interruptions of sector  */
59 /*                                            write)                      */
60 /*    physical_sector_map_entry             Destination for physical      */
61 /*                                            sector map entry address    */
62 /*    physical_sector_address               Destination for physical      */
63 /*                                            sector data                 */
64 /*                                                                        */
65 /*  OUTPUT                                                                */
66 /*                                                                        */
67 /*    return status                                                       */
68 /*                                                                        */
69 /*  CALLS                                                                 */
70 /*                                                                        */
71 /*    _lx_nor_flash_driver_read             Driver flash sector read      */
72 /*    _lx_nor_flash_driver_write            Driver flash sector write     */
73 /*    _lx_nor_flash_system_error            Internal system error handler */
74 /*                                                                        */
75 /*  CALLED BY                                                             */
76 /*                                                                        */
77 /*    Internal LevelX                                                     */
78 /*                                                                        */
79 /*  RELEASE HISTORY                                                       */
80 /*                                                                        */
81 /*    DATE              NAME                      DESCRIPTION             */
82 /*                                                                        */
83 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
84 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
85 /*                                            resulting in version 6.1    */
86 /*  06-02-2021     Bhupendra Naphade        Modified comment(s),          */
87 /*                                            resulting in version 6.1.7  */
88 /*  10-31-2023     Xiuwen Cai               Modified comment(s),          */
89 /*                                            added mapping bitmap cache, */
90 /*                                            added obsolete count cache, */
91 /*                                            optimized full obsoleted    */
92 /*                                            block searching logic,      */
93 /*                                            resulting in version 6.3.0  */
94 /*                                                                        */
95 /**************************************************************************/
_lx_nor_flash_logical_sector_find(LX_NOR_FLASH * nor_flash,ULONG logical_sector,ULONG superceded_check,ULONG ** physical_sector_map_entry,ULONG ** physical_sector_address)96 UINT  _lx_nor_flash_logical_sector_find(LX_NOR_FLASH *nor_flash, ULONG logical_sector, ULONG superceded_check, ULONG **physical_sector_map_entry, ULONG **physical_sector_address)
97 {
98 
99 ULONG                               *block_word_ptr;
100 ULONG                               *list_word_ptr;
101 ULONG                               list_word;
102 ULONG                               min_logical_sector;
103 ULONG                               max_logical_sector;
104 ULONG                               mapped_sectors;
105 ULONG                               total_blocks;
106 ULONG                               total_sectors;
107 ULONG                               i, j;
108 ULONG                               search_start;
109 LX_NOR_SECTOR_MAPPING_CACHE_ENTRY   *sector_mapping_cache_entry_ptr = LX_NULL;
110 LX_NOR_SECTOR_MAPPING_CACHE_ENTRY   temp_sector_mapping_cache_entry;
111 #ifndef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
112 ULONG                               valid_sector_found;
113 #endif
114 #if !defined(LX_DIRECT_READ)  || !defined(LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE)
115 UINT                                status;
116 #endif
117 
118 
119     /* Initialize the return parameters.  */
120     *physical_sector_map_entry =  (ULONG *) 0;
121     *physical_sector_address =    (ULONG *) 0;
122 
123     /* Determine if there are any mapped physical sectors.  */
124     if (nor_flash -> lx_nor_flash_mapped_physical_sectors == 0)
125     {
126 
127         /* No mapped sector so nothing can be found!.  */
128         return(LX_SECTOR_NOT_FOUND);
129     }
130 
131 #ifndef LX_NOR_DISABLE_EXTENDED_CACHE
132 #ifdef LX_NOR_ENABLE_MAPPING_BITMAP
133 
134     /* Determine if the logical sector is in the range of mapping bitmap cache.  */
135     if (logical_sector < nor_flash -> lx_nor_flash_extended_cache_mapping_bitmap_max_logical_sector)
136     {
137 
138         /* Determine if the logical sector is mapped.  */
139         if ((nor_flash -> lx_nor_flash_extended_cache_mapping_bitmap[logical_sector >> 5] & (ULONG)(1 << (logical_sector & 31))) == 0)
140         {
141 
142             /* Not mapped, return not found.  */
143             return(LX_SECTOR_NOT_FOUND);
144         }
145     }
146 #endif
147 #endif
148 
149     /* Determine if the sector mapping cache is enabled.  */
150     if (nor_flash -> lx_nor_flash_sector_mapping_cache_enabled)
151     {
152 
153         /* Calculate the starting index of the sector cache for this sector entry.  */
154         i =  (logical_sector & LX_NOR_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NOR_SECTOR_MAPPING_CACHE_DEPTH;
155 
156         /* Build a pointer to the cache entry.  */
157         sector_mapping_cache_entry_ptr =  &nor_flash -> lx_nor_flash_sector_mapping_cache[i];
158 
159         /* Determine if the sector is in the sector mapping cache - assuming the depth of the sector
160            mapping cache is LX_NOR_SECTOR_MAPPING_CACHE_DEPTH entries.  */
161         if ((sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID))
162         {
163 
164             /* Increment the sector mapping cache hit counter.  */
165             nor_flash -> lx_nor_flash_sector_mapping_cache_hits++;
166 
167             /* Yes, return the cached values associated with the sector.  */
168             *physical_sector_map_entry =  sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_map_entry;
169             *physical_sector_address =    sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_address;
170 
171             /* Don't move anything since we found the entry at the top.  */
172 
173             /* Return a successful status.  */
174             return(LX_SUCCESS);
175         }
176         else if (((sector_mapping_cache_entry_ptr + 1) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID))
177         {
178 
179             /* Increment the sector mapping cache hit counter.  */
180             nor_flash -> lx_nor_flash_sector_mapping_cache_hits++;
181 
182             /* Yes, return the cached values associated with the sector.  */
183             *physical_sector_map_entry =  (sector_mapping_cache_entry_ptr + 1) -> lx_nor_sector_mapping_cache_physical_sector_map_entry;
184             *physical_sector_address =    (sector_mapping_cache_entry_ptr + 1) -> lx_nor_sector_mapping_cache_physical_sector_address;
185 
186             /* Just swap the first and second entry.  */
187             temp_sector_mapping_cache_entry =        *(sector_mapping_cache_entry_ptr);
188             *(sector_mapping_cache_entry_ptr) =      *(sector_mapping_cache_entry_ptr + 1);
189             *(sector_mapping_cache_entry_ptr + 1) =  temp_sector_mapping_cache_entry;
190 
191             /* Return a successful status.  */
192             return(LX_SUCCESS);
193         }
194         else if (((sector_mapping_cache_entry_ptr + 2) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID))
195         {
196 
197             /* Increment the sector mapping cache hit counter.  */
198             nor_flash -> lx_nor_flash_sector_mapping_cache_hits++;
199 
200             /* Yes, return the cached value.  */
201             *physical_sector_map_entry =  (sector_mapping_cache_entry_ptr + 2) -> lx_nor_sector_mapping_cache_physical_sector_map_entry;
202             *physical_sector_address =    (sector_mapping_cache_entry_ptr + 2) -> lx_nor_sector_mapping_cache_physical_sector_address;
203 
204             /* Move the third entry to the top and the first two entries down.  */
205             temp_sector_mapping_cache_entry =        *(sector_mapping_cache_entry_ptr);
206             *(sector_mapping_cache_entry_ptr) =      *(sector_mapping_cache_entry_ptr + 2);
207             *(sector_mapping_cache_entry_ptr + 2) =  *(sector_mapping_cache_entry_ptr + 1);
208             *(sector_mapping_cache_entry_ptr + 1) =  temp_sector_mapping_cache_entry;
209 
210             /* Return a successful status.  */
211             return(LX_SUCCESS);
212         }
213         else if (((sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID))
214         {
215 
216             /* Increment the sector mapping cache hit counter.  */
217             nor_flash -> lx_nor_flash_sector_mapping_cache_hits++;
218 
219             /* Yes, return the cached value.  */
220             *physical_sector_map_entry =  (sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_physical_sector_map_entry;
221             *physical_sector_address =    (sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_physical_sector_address;
222 
223             /* Move the last entry to the top and the first three entries down.  */
224             temp_sector_mapping_cache_entry =        *(sector_mapping_cache_entry_ptr);
225             *(sector_mapping_cache_entry_ptr) =      *(sector_mapping_cache_entry_ptr + 3);
226             *(sector_mapping_cache_entry_ptr + 3) =  *(sector_mapping_cache_entry_ptr + 2);
227             *(sector_mapping_cache_entry_ptr + 2) =  *(sector_mapping_cache_entry_ptr + 1);
228             *(sector_mapping_cache_entry_ptr + 1) =  temp_sector_mapping_cache_entry;
229 
230             /* Return a successful status.  */
231             return(LX_SUCCESS);
232         }
233 
234         /* If we get here, we have a cache miss so increment the counter before we fall through the loop.  */
235         nor_flash -> lx_nor_flash_sector_mapping_cache_misses++;
236     }
237 
238     /* Setup the total number of mapped sectors.  */
239     mapped_sectors =  nor_flash -> lx_nor_flash_mapped_physical_sectors;
240 
241     /* Start searching from the last found block.  */
242     i =  nor_flash -> lx_nor_flash_found_block_search;
243 
244     /* Setup the starting sector to look at.  */
245     j =  nor_flash -> lx_nor_flash_found_sector_search;
246 
247     /* Pickup the total number of blocks.  */
248     total_blocks =  nor_flash -> lx_nor_flash_total_blocks;
249 
250     /* Loop through the blocks to attempt to find the mapped logical sector.  */
251     while (total_blocks--)
252     {
253 
254 #ifdef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
255         /* Determine if the obsolete sector count is available in the cache.  */
256         if (i < nor_flash -> lx_nor_flash_extended_cache_obsolete_count_max_block)
257         {
258 
259             /* Check if the block contains obsolete sectors only.  */
260             if ((ULONG)nor_flash -> lx_nor_flash_extended_cache_obsolete_count[i] == nor_flash -> lx_nor_flash_physical_sectors_per_block)
261             {
262 
263                 /* Move to the next block.  */
264                 i++;
265 
266                 /* Determine if we have wrapped.  */
267                 if (i >= nor_flash -> lx_nor_flash_total_blocks)
268                 {
269 
270                     /* Yes, we have wrapped, set to block 0.  */
271                     i =  0;
272                 }
273 
274                 /* Start at the first sector in the next block.  */
275                 j =  0;
276 
277                 /* No point in looking further into this block, just continue the loop.  */
278                 continue;
279 
280             }
281         }
282 #endif
283 
284         /* Setup the block word pointer to the first word of the search block.  */
285         block_word_ptr =  (nor_flash -> lx_nor_flash_base_address + (i * nor_flash -> lx_nor_flash_words_per_block));
286 
287         /* Determine if the minimum and maximum logical sector values are present in the block header.  If these are
288            present, we can quickly skip blocks that don't have our sector.  */
289 
290         /* Read the minimum and maximum logical sector values in this block.  */
291 #ifdef LX_DIRECT_READ
292 
293         /* Read the word directly.  */
294         min_logical_sector =  *(block_word_ptr + LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET);
295 #else
296         status =  _lx_nor_flash_driver_read(nor_flash, block_word_ptr + LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET, &min_logical_sector, 1);
297 
298         /* Check for an error from flash driver. Drivers should never return an error..  */
299         if (status)
300         {
301 
302             /* Call system error handler.  */
303             _lx_nor_flash_system_error(nor_flash, status);
304 
305             /* Return the error.  */
306             return(status);
307         }
308 #endif
309 
310         /* Is the value valid?  */
311         if (min_logical_sector != LX_ALL_ONES)
312         {
313 #ifdef LX_DIRECT_READ
314 
315             /* Read the word directly.  */
316             max_logical_sector =  *(block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET);
317 #else
318             status =  _lx_nor_flash_driver_read(nor_flash, block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET, &max_logical_sector, 1);
319 
320             /* Check for an error from flash driver. Drivers should never return an error..  */
321             if (status)
322             {
323 
324                 /* Call system error handler.  */
325                 _lx_nor_flash_system_error(nor_flash, status);
326 
327                 /* Return the error.  */
328                 return(status);
329             }
330 #endif
331 
332             /* Is the value valid?  */
333             if (max_logical_sector != LX_ALL_ONES)
334             {
335 
336                 /* Now let's check to see if the search sector is within this range.  */
337                 if ((logical_sector < min_logical_sector) || (logical_sector > max_logical_sector))
338                 {
339 
340                     /* Move to the next block.  */
341                     i++;
342 
343                     /* Determine if we have wrapped.  */
344                     if (i >= nor_flash -> lx_nor_flash_total_blocks)
345                     {
346 
347                         /* Yes, we have wrapped, set to block 0.  */
348                         i =  0;
349                     }
350 
351                     /* Start at the first sector in the next block.  */
352                     j =  0;
353 
354                     /* No point in looking further into this block, just continue the loop.  */
355                     continue;
356                 }
357             }
358         }
359         else
360         {
361 
362             /* Set the max logical sector to all ones.  */
363             max_logical_sector = LX_ALL_ONES;
364         }
365 #ifndef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
366 
367         /* Clear the valid sector found flag.  */
368         valid_sector_found = LX_FALSE;
369 #endif
370 
371         /* Setup the total number of sectors.  */
372         total_sectors =  nor_flash -> lx_nor_flash_physical_sectors_per_block;
373 
374         /* Remember the start of the search.  */
375         search_start =  j;
376 
377         /* Now search through the sector list to find a match.  */
378         while (total_sectors--)
379         {
380 
381             /* Setup a pointer to the mapped list.  */
382             list_word_ptr =  block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j;
383 
384 
385             /* Read in the mapped list for this block.  */
386 #ifdef LX_DIRECT_READ
387 
388             /* Read the word directly.  */
389             list_word =  *(list_word_ptr);
390 #else
391             status =  _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_word, 1);
392 
393             /* Check for an error from flash driver. Drivers should never return an error..  */
394             if (status)
395             {
396 
397                 /* Call system error handler.  */
398                 _lx_nor_flash_system_error(nor_flash, status);
399 
400                 /* Return the error.  */
401                 return(status);
402             }
403 #endif
404 
405             /* Determine if the entry hasn't been used.  */
406             if (list_word == LX_NOR_PHYSICAL_SECTOR_FREE)
407             {
408 
409                 /* Since the mapping is done sequentially in the block, we know nothing
410                    else exists after this point.  */
411 
412                 /* Determine if the search started at the beginning of the block.  */
413                 if (search_start == 0)
414                 {
415 
416                     /* Yes, we started at the beginning of the block.  We are now done with this block. */
417                     break;
418                 }
419                 else
420                 {
421 
422                     /* Setup the new total to the search start.  */
423                     total_sectors =  search_start;
424 
425                     /* Clear search start.  */
426                     search_start =  0;
427 
428                     /* Start search over.  */
429                     j =  0;
430                     continue;
431                 }
432             }
433 
434             /* Is this entry valid?  */
435             if ((list_word & (LX_NOR_PHYSICAL_SECTOR_VALID | LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID)) == LX_NOR_PHYSICAL_SECTOR_VALID)
436             {
437 
438                 /* Decrement the number of mapped sectors.  */
439                 mapped_sectors--;
440 
441                 /* Do we have a valid sector match?  */
442                 if ((list_word & LX_NOR_LOGICAL_SECTOR_MASK) == logical_sector)
443                 {
444 
445                     /* Determine if we care about the superceded bit.  */
446                     if (superceded_check == LX_FALSE)
447                     {
448 
449                         /* Prepare the return information.  */
450                         *physical_sector_map_entry =  list_word_ptr;
451                         *physical_sector_address =    block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset + (j * LX_NOR_SECTOR_SIZE);
452 
453                         /* Determine if the sector mapping cache is enabled.  */
454                         if (nor_flash -> lx_nor_flash_sector_mapping_cache_enabled)
455                         {
456 
457                             /* Yes, update the cache with the sector mapping.  */
458 
459                             /* Move all the cache entries down so the oldest is at the bottom.  */
460                             *(sector_mapping_cache_entry_ptr + 3) =  *(sector_mapping_cache_entry_ptr + 2);
461                             *(sector_mapping_cache_entry_ptr + 2) =  *(sector_mapping_cache_entry_ptr + 1);
462                             *(sector_mapping_cache_entry_ptr + 1) =  *(sector_mapping_cache_entry_ptr);
463 
464                             /* Setup the new sector information in the cache.  */
465                             sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_logical_sector =             (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID);
466                             sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_map_entry =  *physical_sector_map_entry;
467                             sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_address =    *physical_sector_address;
468                         }
469 
470                         /* Remember the last found block for next search.  */
471                         nor_flash -> lx_nor_flash_found_block_search =  i;
472 
473                         /* Remember the last found sector.  */
474                         nor_flash -> lx_nor_flash_found_sector_search =  j+1;
475 
476                         /* Has this wrapped around?  */
477                         if (nor_flash -> lx_nor_flash_found_sector_search >= nor_flash -> lx_nor_flash_physical_sectors_per_block)
478                         {
479 
480                             /* Reset to the beginning sector.  */
481                             nor_flash -> lx_nor_flash_found_sector_search =  0;
482                         }
483 
484                         /* Return success!  */
485                         return(LX_SUCCESS);
486                     }
487 
488                     /* Check for the superceded bit being clear, which means the sector was superceded.  */
489                     else if (list_word & LX_NOR_PHYSICAL_SECTOR_SUPERCEDED)
490                     {
491 
492                         /* Prepare the return information.  */
493                         *physical_sector_map_entry =  list_word_ptr;
494                         *physical_sector_address =    block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset + (j * LX_NOR_SECTOR_SIZE);
495 
496                         /* No need to update the cache here, since this condition only happens during initialization.  */
497 
498                         /* Remember the last found block for next search.  */
499                         nor_flash -> lx_nor_flash_found_block_search =  i;
500 
501                         /* Remember the last found sector.  */
502                         nor_flash -> lx_nor_flash_found_sector_search =  j+1;
503 
504                         /* Has this wrapped around?  */
505                         if (nor_flash -> lx_nor_flash_found_sector_search >= nor_flash -> lx_nor_flash_physical_sectors_per_block)
506                         {
507 
508                             /* Reset to the beginning sector.  */
509                             nor_flash -> lx_nor_flash_found_sector_search =  0;
510                         }
511 
512                         /* Return success!  */
513                         return(LX_SUCCESS);
514                     }
515                 }
516 #ifndef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
517 
518                 /* Set the valid sector found flag.  */
519                 valid_sector_found = LX_TRUE;
520 #endif
521             }
522 
523             /* Move to the next list entry.  */
524             j++;
525 
526             /* Check for wrap around.  */
527             if (j >= nor_flash -> lx_nor_flash_physical_sectors_per_block)
528             {
529 
530                 /* Yes, wrap around, go back to the beginning.  */
531                 j =  0;
532             }
533         }
534 #ifndef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
535         /* Check if the block contains no valid sectors.  */
536         if ((valid_sector_found == LX_FALSE) && (max_logical_sector != LX_ALL_ONES))
537         {
538 
539                 /* Clear max logical sector to indicate sectors are all obsoleted.  */
540                 max_logical_sector = 0;
541 
542                 /* Write the max logical sector to the block header.  */
543                 status =  _lx_nor_flash_driver_write(nor_flash, block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET, &max_logical_sector, 1);
544 
545                 /* Check for an error from flash driver. Drivers should never return an error..  */
546                 if (status)
547                 {
548 
549                     /* Call system error handler.  */
550                     _lx_nor_flash_system_error(nor_flash, status);
551 
552                     /* Return the error.  */
553                     return(status);
554                 }
555         }
556 #endif
557 
558         /* Determine if there are any more mapped sectors.  */
559         if (mapped_sectors == 0)
560             break;
561 
562         /* Move to the next block.  */
563         i++;
564 
565         /* Determine if we have wrapped.  */
566         if (i >= nor_flash -> lx_nor_flash_total_blocks)
567         {
568 
569             /* Yes, we have wrapped, set to block 0.  */
570             i =  0;
571         }
572 
573         /* Start at the first sector in the next block.  */
574         j =  0;
575     }
576 
577     /* Return sector not found status.  */
578     return(LX_SECTOR_NOT_FOUND);
579 }
580 
581