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