1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ndmp.h"
6 
7 #if INC_NDM
8 #include <kprivate/fsprivate.h>
9 
10 // Configuration
11 #define BBL_INSERT_INC TRUE
12 #define BBL_INSERT_DEBUG FALSE
13 
14 // Symbol Definitions
15 
16 //
17 // Reason for get_pbn() virtual to physical block mapping
18 //
19 #define WR_MAPPING 0
20 #define RD_MAPPING 1
21 
22 // Global Data Definitions
23 #if BBL_INSERT_INC
24 static ui32 ExtractedCnt;
25 static Pair* ExtractedList;
26 #endif
27 
28 // Local Function Definitions
29 
30 #if BBL_INSERT_DEBUG
31 //   show_rbbl: Show NDM's running bad block list
32 //
33 //      Inputs: list = pointer to NDM control block running BB list
34 //              cnt = number of bad blocks in list
35 //
show_rbbl(Pair * list,ui32 cnt)36 static void show_rbbl(Pair* list, ui32 cnt) {
37     Pair *past_end, *pair = list;
38 
39     putchar('\n');
40     for (past_end = pair + cnt; pair < past_end; ++pair)
41         printf("pair %u: vblk/key=%u, pblk/val=%d\n", pair - list, pair->key, pair->val);
42 }
43 #endif
44 
45 // get_ctrl_size: Count number of pages needed to write current
46 //              control information
47 //
48 //       Input: ndm = pointer to NDM control block
49 //
50 //     Returns: control info size in pages on success, 0 on failure
51 //
get_ctrl_size(CNDM ndm)52 static int get_ctrl_size(CNDM ndm) {
53     ui32 i, curr_loc = CTRL_DATA_START, num_pages = 0;
54 
55     // Figure out how many control pages are needed. Each control page
56     // has a header (HDR_SIZE bytes). Control information has the
57     // following preamble:
58     //   - device number of blocks + block size (8 bytes = 2 ui32s)
59     //   - control block pointers (2)           (8 bytes = 2 ui32s)
60     //   - free block/ctrl block pointers       (8 bytes = 2 ui32s)
61     //   - number of partitions                 (4 bytes = 1 ui32)
62     //   - on normal write
63     //       - invalid transfer to block        (4 bytes = 1 ui32)
64     //   - on bad block transfer
65     //       - transfer to block                (4 bytes = 1 ui32)
66     //       - transferred block                (4 bytes = 1 ui32)
67     //       - bad page in transferred block    (4 bytes = 1 ui32)
68     //       - partial/full scan flag           (1 byte)
69     curr_loc += 8 * sizeof(ui32);
70     if (ndm->xfr_tblk != (ui32)-1)
71         curr_loc += 2 * sizeof(ui32) + 1;
72 
73     // After the preamble, the initial bad block map is written. Figure
74     // out how much space it takes.
75     for (i = 0;; ++i) {
76         // Ensure the initial bad block map is valid.
77         if (i > ndm->max_bad_blks) {
78             FsError(EINVAL);
79             return 0;
80         }
81 
82         // Add current entry.
83         if (curr_loc + sizeof(ui32) > ndm->page_size) {
84             ++num_pages;
85             curr_loc = CTRL_DATA_START;
86         }
87         curr_loc += sizeof(ui32);
88 
89         // If we've reached end of initial bad block map, stop.
90         if (ndm->init_bad_blk[i] == ndm->num_dev_blks)
91             break;
92     }
93 
94     // The running bad block map is written next. Determine its size.
95     for (i = 0;; ++i) {
96         // Ensure running bad block map is valid.
97         if (i > ndm->max_bad_blks) {
98             FsError(EINVAL);
99             return 0;
100         }
101 
102         // Add current entry.
103         if (curr_loc + 2 * sizeof(ui32) > ndm->page_size) {
104             ++num_pages;
105             curr_loc = CTRL_DATA_START;
106         }
107         curr_loc += 2 * sizeof(ui32);
108 
109         // If we've reached the end of running bad block map, stop.
110         if (i == ndm->num_rbb) {
111             if (curr_loc + 2 * sizeof(ui32) > ndm->page_size) {
112                 ++num_pages;
113                 curr_loc = CTRL_DATA_START;
114             }
115             curr_loc += 2 * sizeof(ui32);
116             break;
117         }
118     }
119 
120     // The partitions are written. Figure out how much space they take.
121     for (i = 0; i < ndm->num_partitions; ++i) {
122         if (curr_loc + sizeof(NDMPartition) > ndm->page_size) {
123             ++num_pages;
124             curr_loc = CTRL_DATA_START;
125         }
126         curr_loc += sizeof(NDMPartition);
127     }
128 
129     // If last control page will be partially written, account for it.
130     if (curr_loc > CTRL_DATA_START)
131         ++num_pages;
132 
133     // Output number of control pages and return success.
134     return num_pages;
135 }
136 
137 // wr_ctrl_page: Write a page of control information to flash
138 //
139 //      Inputs: ndm = pointer to NDM control block
140 //              cpc = current control page count
141 //   In/Output: *curr_pnp = page number where next page is written
142 //      Output: *badblkp = bad block number (if page write fails)
143 //
144 //     Returns: 0 if successful, -1 if block failed while writing, -2
145 //              if fatal error
146 //
wr_ctrl_page(NDM ndm,ui32 cpc,ui32 * curr_pnp,ui32 * badblkp)147 static int wr_ctrl_page(NDM ndm, ui32 cpc, ui32* curr_pnp, ui32* badblkp) {
148     ui32 cpn = *curr_pnp, crc = CRC32_START, i;
149     int rc;
150 
151     // Fill current page count in header.
152     WR16_LE(cpc, &ndm->main_buf[HDR_CURR_LOC]);
153 
154     // Perform CRC over page contents and write it on the page.
155     for (i = 0; i < ndm->page_size; ++i) {
156         if (i == HDR_CRC_LOC)
157             i = CTRL_DATA_START;
158         crc = CRC32_UPDATE(crc, ndm->main_buf[i]);
159     }
160     crc = ~crc;
161     WR32_LE(crc, &ndm->main_buf[HDR_CRC_LOC]); //lint !e850
162 
163     // Write page to flash. Check for error indication.
164     rc = ndm->write_page(cpn, ndm->main_buf, ndm->spare_buf, NDM_ECC_VAL, ndm->dev);
165     if (rc) {
166         // If fatal error, return fatal error indication.
167         if (rc == -2) {
168             FsError(EIO);
169             return rc;
170         }
171 
172         // Else bad block caused write failure, output block number and
173         // return bad block indication.
174         else {
175             PfAssert(rc == -1);
176 #if NDM_DEBUG
177             printf("wr_ctrl_page: bad block for #%u at page #%u\n", cpc, cpn);
178 #endif
179             *badblkp = cpn / ndm->pgs_per_blk;
180             return -1;
181         }
182     }
183 
184     // Update first and/or last control page.
185     if (cpc == 1) {
186         ndm->frst_ctrl_page = cpn;
187 #if NV_NDM_CTRL_STORE
188         NvNdmCtrlPgWr(cpn);
189 #endif
190     }
191     if (cpc == ndm->ctrl_pages)
192         ndm->last_ctrl_page = cpn;
193 #if NDM_DEBUG
194     printf("wr_ctrl_page: wrote %u at page %u (block %u)\n", cpc, cpn, cpn / ndm->pgs_per_blk);
195 #endif
196 
197     // Advance to next page to write control information into.
198     // Just increment page number if not on last page of block.
199     if ((cpn + 1) % ndm->pgs_per_blk)
200         ++cpn;
201 
202     // Else prepare to switch to new control page.
203     else {
204         // Switch to first page on opposing control block.
205         if (cpn / ndm->pgs_per_blk == ndm->ctrl_blk0)
206             cpn = ndm->ctrl_blk1 * ndm->pgs_per_blk;
207         else
208             cpn = ndm->ctrl_blk0 * ndm->pgs_per_blk;
209 
210         // Erase new block now, before its first write. Check for error.
211         rc = ndm->erase_block(cpn, ndm->dev);
212         if (rc) {
213             // If fatal error, return fatal error indication.
214             if (rc == -2) {
215                 FsError(EIO);
216                 return rc;
217             }
218 
219             // Else bad block caused erase failure, output block number and
220             // return bad block indication.
221             else {
222                 PfAssert(rc == -1);
223 #if NDM_DEBUG
224                 printf("wr_ctrl_page: bad block for #%u at page #%u\n", cpc, cpn);
225 #endif
226                 *badblkp = cpn / ndm->pgs_per_blk;
227                 return -1;
228             }
229         }
230     }
231 
232     // Output page number for next control write and return success.
233     *curr_pnp = cpn;
234     return 0;
235 }
236 
237 // wr_ctrl_info: Write NDM control information
238 //
239 //      Inputs: ndm = pointer to NDM control block
240 //              frst_page = first page to write to
241 //      Output: *badblkp = number of bad control block (if any)
242 //
243 //     Returns: 0 if successful, -1 if block failed, -2 if fatal error
244 //
wr_ctrl_info(NDM ndm,ui32 frst_page,ui32 * badblkp)245 static int wr_ctrl_info(NDM ndm, ui32 frst_page, ui32* badblkp) {
246     ui32 i, curr_loc, write_count = 1, cpn = frst_page;
247     int status;
248 
249     // Determine control information size as number of pages.
250     ndm->ctrl_pages = get_ctrl_size(ndm);
251     if (ndm->ctrl_pages == 0)
252         return -2;
253 #if NDM_DEBUG
254     printf("wr_ctrl_inf: preparing to write %u NDM ctrl pages\n", ndm->ctrl_pages);
255 #endif
256 
257     // Initialize the spare area: 0xFF except for the signature bytes
258     // and NDM control page mark.
259     memset(ndm->spare_buf, 0xFF, ndm->eb_size);
260     memcpy(&ndm->spare_buf[EB_FRST_RESERVED], CTRL_SIG, CTRL_SIG_SZ);
261     ndm->spare_buf[EB_REG_MARK] = 0;
262 
263     // Initialize main page with 0xFF.
264     memset(ndm->main_buf, 0xFF, ndm->page_size);
265 
266     // Set the constant part of the control page header: last location
267     // and sequence number. The current location varies with page number.
268     WR16_LE(ndm->ctrl_pages, &ndm->main_buf[HDR_LAST_LOC]);
269     ++ndm->ctrl_seq;
270     WR32_LE(ndm->ctrl_seq, &ndm->main_buf[HDR_SEQ_LOC]);
271 
272     // Set the first control page data, starting with the device size.
273     curr_loc = CTRL_DATA_START;
274     WR32_LE(ndm->num_dev_blks, &ndm->main_buf[curr_loc]);
275     curr_loc += sizeof(ui32);
276     WR32_LE(ndm->block_size, &ndm->main_buf[curr_loc]);
277     curr_loc += sizeof(ui32);
278 
279     // Write the control block numbers.
280     WR32_LE(ndm->ctrl_blk0, &ndm->main_buf[curr_loc]);
281     curr_loc += sizeof(ui32);
282     WR32_LE(ndm->ctrl_blk1, &ndm->main_buf[curr_loc]);
283     curr_loc += sizeof(ui32);
284 
285     // Add free_virt_blk pointer.
286     WR32_LE(ndm->free_virt_blk, &ndm->main_buf[curr_loc]);
287     curr_loc += sizeof(ui32);
288 
289     // Add free_ctrl_blk number.
290     WR32_LE(ndm->free_ctrl_blk, &ndm->main_buf[curr_loc]);
291     curr_loc += sizeof(ui32);
292 
293 #if NDM_DEBUG
294     puts("wr_ctrl_inf:");
295     printf("  -> ctrl_seq    = %u\n", ndm->ctrl_seq);
296     printf("  -> ctrl_blk0   = %u\n", ndm->ctrl_blk0);
297     printf("  -> ctrl_blk1   = %u\n", ndm->ctrl_blk1);
298     printf("  -> free_virt_blk = %u\n", ndm->free_virt_blk);
299     printf("  -> free_ctrl_blk = %u\n", ndm->free_ctrl_blk);
300 #endif
301 
302     // Add 'transfer to' physical block number value/flag.
303     WR32_LE(ndm->xfr_tblk, &ndm->main_buf[curr_loc]);
304     curr_loc += sizeof(ui32);
305 
306     // If bad block transfer, add remaining bad block transfer info.
307     if (ndm->xfr_tblk != (ui32)-1) {
308         // Add physical block number of the bad block being transferred.
309         WR32_LE(ndm->xfr_fblk, &ndm->main_buf[curr_loc]);
310         curr_loc += sizeof(ui32);
311 
312         // Add page offset of bad page in bad block.
313         WR32_LE(ndm->xfr_bad_po, &ndm->main_buf[curr_loc]);
314         curr_loc += sizeof(ui32);
315 
316         // Add (legacy) partial scan flag.
317         ndm->main_buf[curr_loc++] = PARTIAL_SCAN;
318 
319 #if NDM_DEBUG
320         printf("  -> xfr_tblk       = %u\n", ndm->xfr_tblk);
321         printf("  -> xfr_fblk       = %u\n", ndm->xfr_fblk);
322         printf("  -> xfr_bad_po     = %u\n", ndm->xfr_bad_po);
323 #endif
324     }
325 #if NDM_DEBUG
326     else
327         puts("  -> xfr_tblk      = -1");
328 #endif
329 
330     // Write the number of partitions.
331     WR32_LE(ndm->num_partitions, &ndm->main_buf[curr_loc]);
332     curr_loc += sizeof(ui32);
333 #if NDM_DEBUG
334     printf("  -> num_partitions = %u\n", ndm->num_partitions);
335 #endif
336 
337     // Now write the initial bad block map.
338     for (i = 0;; ++i) {
339         // If next write would go past end of control page, write page.
340         if (curr_loc + sizeof(ui32) > ndm->page_size) {
341             status = wr_ctrl_page(ndm, write_count++, &cpn, badblkp);
342             if (status)
343                 return status;
344             curr_loc = CTRL_DATA_START;
345         }
346 
347         // Add next bad block.
348         WR32_LE(ndm->init_bad_blk[i], &ndm->main_buf[curr_loc]);
349         curr_loc += sizeof(ui32);
350 
351         // If end of map is reached, stop.
352         if (ndm->init_bad_blk[i] == ndm->num_dev_blks)
353             break;
354 
355 #if NDM_DEBUG
356         printf("  -> init_bad_blk[%2u] = %u\n", i, ndm->init_bad_blk[i]);
357 #endif
358     }
359 
360     // Now write the running bad block map.
361     for (i = 0;; ++i) {
362         // If next write would go past end of control page, write page.
363         if (curr_loc + 2 * sizeof(ui32) > ndm->page_size) {
364             status = wr_ctrl_page(ndm, write_count++, &cpn, badblkp);
365             if (status)
366                 return status;
367             curr_loc = CTRL_DATA_START;
368         }
369 
370         // If end of running bad blocks map reached, mark end.
371         if (i == ndm->num_rbb) {
372             // Mark end of map before writing whole map to flash.
373             WR32_LE((ui32)-1, &ndm->main_buf[curr_loc]);
374             curr_loc += sizeof(ui32);
375             WR32_LE((ui32)-1, &ndm->main_buf[curr_loc]);
376             curr_loc += sizeof(ui32);
377             break;
378         }
379 
380         // Add next running bad block pair.
381         WR32_LE(ndm->run_bad_blk[i].key, &ndm->main_buf[curr_loc]);
382         curr_loc += sizeof(ui32);
383         WR32_LE(ndm->run_bad_blk[i].val, &ndm->main_buf[curr_loc]);
384         curr_loc += sizeof(ui32);
385 
386 #if NDM_DEBUG
387         printf("  -> run_bad_blk[%2u]: key = %u, val = %u\n", i, ndm->run_bad_blk[i].key,
388                ndm->run_bad_blk[i].val);
389 #endif
390     }
391 
392     // Write the NDM partitions if any, one at a time.
393     for (i = 0; i < ndm->num_partitions; ++i) {
394 #if NDM_PART_USER
395         ui32 j;
396 #endif
397 
398         // If next write would go past end of control page, write page.
399         if (curr_loc + sizeof(NDMPartition) > ndm->page_size) {
400             status = wr_ctrl_page(ndm, write_count++, &cpn, badblkp);
401             if (status)
402                 return status;
403             curr_loc = CTRL_DATA_START;
404         }
405 
406         // Write partition first block.
407         WR32_LE(ndm->partitions[i].first_block, &ndm->main_buf[curr_loc]);
408         curr_loc += sizeof(ui32);
409 
410         // Write partition number of blocks.
411         WR32_LE(ndm->partitions[i].num_blocks, &ndm->main_buf[curr_loc]);
412         curr_loc += sizeof(ui32);
413 
414 #if NDM_PART_USER
415         // Write the user defined ui32s.
416         for (j = 0; j < NDM_PART_USER; ++j) {
417             WR32_LE(ndm->partitions[i].user[j], &ndm->main_buf[curr_loc]);
418             curr_loc += sizeof(ui32);
419         }
420 #endif
421 
422         // Write the partition name.
423         strncpy((char*)&ndm->main_buf[curr_loc], ndm->partitions[i].name, NDM_PART_NAME_LEN);
424         curr_loc += NDM_PART_NAME_LEN;
425 
426         // Write the partition type.
427         ndm->main_buf[curr_loc++] = ndm->partitions[i].type;
428 
429 #if NDM_DEBUG
430         printf("  -> partition[%2u]:\n", i);
431         printf("    - name        = %s\n", ndm->partitions[i].name);
432         printf("    - first block = %u\n", ndm->partitions[i].first_block);
433         printf("    - num blocks  = %u\n", ndm->partitions[i].num_blocks);
434 #if NDM_PART_USER
435         for (j = 0; j < NDM_PART_USER; ++j)
436             printf("    - user[%u]     = %u\n", j, ndm->partitions[i].user[j]);
437 #endif
438         printf("    - type        = ");
439         switch (ndm->partitions[i].type) {
440             case 0:
441                 puts("NONE");
442                 break;
443             case FFS_VOL:
444                 puts("FFS");
445                 break;
446             case FAT_VOL:
447                 puts("FAT");
448                 break;
449             default:
450                 printf("%u\n", ndm->partitions[i].type);
451                 break;
452         }
453 #endif // NDM_DEBUG
454     }
455 
456     // Write last control page and return status.
457     return wr_ctrl_page(ndm, write_count, &cpn, badblkp);
458 }
459 
460 // mark_ctrl_bblock: Record a control block failure and get a new one
461 //
462 //      Inputs: ndm = pointer to NDM control block
463 //              *cblkp = control block that failed
464 //      Output: *cblkp = new control block
465 //
466 //     Returns: 0 on success, -1 on failure
467 //
mark_ctrl_bblock(NDM ndm,ui32 * cblkp)468 static int mark_ctrl_bblock(NDM ndm, ui32* cblkp) {
469     ui32 bad_blk = *cblkp;
470 
471     // Clear virtual to physical translation caches.
472     ndm->last_wr_vbn = ndm->last_rd_vbn = (ui32)-1;
473 
474     // Adjust bad block count. If too many, error.
475     if (++ndm->num_bad_blks > ndm->max_bad_blks)
476         return FsError(ENOSPC);
477 
478     // Update the running bad block map with this block.
479     ndm->run_bad_blk[ndm->num_rbb].key = bad_blk;
480     ndm->run_bad_blk[ndm->num_rbb].val = (ui32)-1;
481     ++ndm->num_rbb;
482 
483     // Get a new free block for the other control block.
484     for (; ndm->free_ctrl_blk != (ui32)-1; --ndm->free_ctrl_blk) {
485         // If we've ran out of free blocks, error.
486         if (ndm->free_ctrl_blk < ndm->free_virt_blk) {
487             ndm->free_ctrl_blk = ndm->free_virt_blk = (ui32)-1;
488             break;
489         }
490 
491         // If initial bad block, skip it.
492         if (ndmInitBadBlock(ndm, ndm->free_ctrl_blk))
493             continue;
494 
495         // We found a free block. Remember it.
496         *cblkp = ndm->free_ctrl_blk;
497 
498         // Update the right control block pointer with this free block.
499         if (bad_blk == ndm->ctrl_blk0)
500             ndm->ctrl_blk0 = ndm->free_ctrl_blk;
501         else if (bad_blk == ndm->ctrl_blk1)
502             ndm->ctrl_blk1 = ndm->free_ctrl_blk;
503         else
504             return FsError(EINVAL);
505 
506         // Update pointer to next free control block and return success.
507         --ndm->free_ctrl_blk;
508         return 0;
509     }
510 
511     // If no free block found for control block, error.
512     return FsError(ENOSPC);
513 }
514 
515 // get_free_virt_blk: Get next free block reserved for replacing bad
516 //              virtual blocks (starts at lowest and goes up)
517 //
518 //       Input: ndm = pointer to NDM control block
519 //
520 //     Returns: Free block if successful, (ui32)-1 if none are free
521 //
get_free_virt_blk(NDM ndm)522 static ui32 get_free_virt_blk(NDM ndm) {
523     ui32 free_b = ndm->free_virt_blk;
524 
525     // Check if there are any free blocks left.
526     if (free_b != (ui32)-1) {
527         ui32 b = free_b + 1;
528 
529         // Skip past initial bad blocks.
530         while (b <= ndm->free_ctrl_blk && ndmInitBadBlock(ndm, b)) ++b;
531 
532         // If we haven't gone into the blocks reserved for swapping bad
533         // control blocks, update the free block pointer.
534         if (b <= ndm->free_ctrl_blk)
535             ndm->free_virt_blk = b;
536         else
537             ndm->free_virt_blk = ndm->free_ctrl_blk = (ui32)-1;
538     }
539 
540     // Return free block number.
541     return free_b;
542 }
543 
544 // mark_extra_bblock: Mark a block bad while in middle of a transfer
545 //              of another bad block (this is the transfer to block)
546 //
547 //      Inputs: ndm = pointer to NDM control block
548 //   In/Output: *bnp = bad block IN, substitute free block OUT
549 //
550 //     Returns: 0 on success, -1 on failure
551 //
mark_extra_bblock(NDM ndm,ui32 * bnp)552 static int mark_extra_bblock(NDM ndm, ui32* bnp) {
553     ui32 free_b, i;
554     int found;
555 
556     // Clear virtual to physical translation caches.
557     ndm->last_wr_vbn = ndm->last_rd_vbn = (ui32)-1;
558 
559     // Adjust number of bad blocks. If too many, error.
560     if (++ndm->num_bad_blks > ndm->max_bad_blks)
561         return FsError(ENOSPC);
562 
563     // Get a free block to replace the bad block.
564     free_b = get_free_virt_blk(ndm);
565     if (free_b == (ui32)-1)
566         return FsError(ENOSPC);
567 
568     // Check that bad block is in the running bad block map only as a
569     // transfer to block (.val field) and only once.
570     for (i = 0, found = FALSE;; ++i) {
571         // If end of map, either found exactly once or error.
572         if (i == ndm->num_rbb) {
573             if (found)
574                 break;
575             return FsError(EINVAL);
576         }
577 
578         // Bad block cannot be in the map as a transfer from block (.key).
579         if (ndm->run_bad_blk[i].key == *bnp)
580             return FsError(EINVAL);
581 
582         // If bad block in map, ensure first occurrence and remember find.
583         if (ndm->run_bad_blk[i].val == *bnp) {
584             if (found)
585                 return FsError(EINVAL);
586             found = TRUE;
587         }
588     }
589 
590     // Add new entry to running bad block map with bad block.
591     ndm->run_bad_blk[ndm->num_rbb].key = *bnp;
592     ndm->run_bad_blk[ndm->num_rbb].val = free_b;
593     ++ndm->num_rbb;
594 
595     // Output the selected free block number and return success.
596     *bnp = free_b;
597     return 0;
598 }
599 
600 // run_bad_block: Check if a block is a running bad block
601 //
602 //      Inputs: ndm = pointer to NDM control block
603 //              b = block to check
604 //
605 //     Returns: TRUE if block is bad, FALSE otherwise
606 //
run_bad_block(CNDM ndm,ui32 b)607 static int run_bad_block(CNDM ndm, ui32 b) {
608     ui32 i;
609 
610     // Walk the run bad blocks map to see if block is in there.
611     for (i = 0; i < ndm->num_rbb; ++i)
612         if (ndm->run_bad_blk[i].key == b)
613             return TRUE;
614 
615     // Block number not found in list. Return FALSE.
616     return FALSE;
617 }
618 
619 //     get_pbn: Get a physical block number from a virtual one
620 //
621 //      Inputs: ndm = pointer to NDM control block
622 //              vbn = virtual block number
623 //              reason = reason for lookup: RD_MAPPING or WR_MAPPING
624 //
625 //     Returns: physical block number on success, else (ui32)-1
626 //
get_pbn(NDM ndm,ui32 vbn,int reason)627 static ui32 get_pbn(NDM ndm, ui32 vbn, int reason) {
628     ui32 bn, i;
629 #if NDM_DEBUG
630     ui32 j;
631 
632     // Ensure the initial bad blocks map is valid (no duplicates).
633     for (i = 0;; ++i) {
634         if (i > ndm->max_bad_blks)
635             return (ui32)-1;
636         if (ndm->init_bad_blk[i] == ndm->num_dev_blks)
637             break;
638         for (j = i + 1; j <= ndm->max_bad_blks; ++j) {
639             if (ndm->init_bad_blk[j] == ndm->num_dev_blks)
640                 break;
641             if (ndm->init_bad_blk[i] == ndm->init_bad_blk[j])
642                 return (ui32)-1;
643         }
644     }
645 
646     // Ensure running bad blocks map is valid (no key/value duplicates).
647     for (i = 0; i < ndm->num_rbb; ++i) {
648         if (ndm->run_bad_blk[i].key == (ui32)-1)
649             return (ui32)-1;
650         for (j = i + 1; j < ndm->num_rbb; ++j) {
651             if (ndm->run_bad_blk[i].key == ndm->run_bad_blk[j].key)
652                 return (ui32)-1;
653             if (ndm->run_bad_blk[i].val == ndm->run_bad_blk[j].val &&
654                 ndm->run_bad_blk[i].val != (ui32)-1)
655                 return (ui32)-1;
656         }
657     }
658 #endif // NDM_DEBUG
659 
660     // If no bad blocks, physical block number matches virtual.
661     if (ndm->num_bad_blks == 0)
662         return vbn;
663 
664     // If virtial block number matches last read or write lookup, return
665     // last read or write physical block number.
666     if (vbn == ndm->last_wr_vbn)
667         return ndm->last_wr_pbn;
668     if (vbn == ndm->last_rd_vbn)
669         return ndm->last_rd_pbn;
670 
671     // First determine where the block was before any running bad blocks
672     // happened. Do this by walking the initial bad blocks map.
673     for (i = 0, bn = vbn;; ++i) {
674         // Should never pass last entry, which holds 'num_dev_blks'.
675         if (i > ndm->max_bad_blks)
676             return (ui32)FsError(EINVAL);
677 
678         // 'i' is the number of initial bad blocks preceding the indexed`
679         // initial bad block. Break when the number of volume blocks and
680         // skipped bad blocks is less than the indexed initial bad block.
681         if (vbn + i < ndm->init_bad_blk[i]) {
682             // Add 'i' to account for the initial bad blocks that precede
683             // this block. This mapping of the initial bad blocks supports
684             // use of images programmed using the 'skip bad block' method.
685             bn += i;
686             break;
687         }
688     }
689 
690     // At this point the initial bad block cannot be in the NDM area.
691     if (bn >= ndm->frst_reserved)
692         return (ui32)FsError(EINVAL);
693 
694     // Walk the running bad block map replacing initial block with the
695     // most current version (if any).
696     for (i = 0; i < ndm->num_rbb; ++i)
697         if (ndm->run_bad_blk[i].key == bn)
698             bn = ndm->run_bad_blk[i].val;
699 
700     // Ensure the block number is valid.
701     if (bn >= ndm->num_dev_blks)
702         return (ui32)FsError(EINVAL);
703 
704     // Update last read or write lookup cache.
705     if (reason == WR_MAPPING) {
706         ndm->last_wr_pbn = bn;
707         ndm->last_wr_vbn = vbn;
708     } else // reason == RD_MAPPING
709     {
710         ndm->last_rd_pbn = bn;
711         ndm->last_rd_vbn = vbn;
712     }
713 
714     // Return physical block number.
715     return bn;
716 }
717 
718 #if INC_FFS_NDM && INC_FTL_NDM
719 // get_part_type: Find out the partition type based on a block in that
720 //              partition
721 //
722 //      Inputs: ndm = pointer to NDM control block
723 //              bn = physical block number
724 //
725 //     Returns: Partition type if block is in a partition, 0 otherwise
726 //
get_part_type(CNDM ndm,ui32 bn)727 static ui8 get_part_type(CNDM ndm, ui32 bn) {
728     ui32 i, vbn;
729 #if NDM_DEBUG
730     ui32 new_bn;
731 #endif
732 
733     // While block is in the NDM area, either it's a replacement for a
734     // partition block or it's an internal NDM control block. Search the
735     // NDM running bad blocks list to determine which of the two it is.
736     while (bn >= ndm->frst_reserved) {
737         // Walk the running bad block list to find if block belongs in it.
738         for (i = 0;; ++i) {
739             // If end of list is reached, block was not on it. This means
740             // block belongs to the NDM. Return 0.
741             if (i == ndm->num_rbb)
742                 return 0;
743 
744             // If block found, continue search using the block it replaced.
745             if (ndm->run_bad_blk[i].val == bn) {
746                 bn = ndm->run_bad_blk[i].key;
747                 break;
748             }
749         }
750     }
751 
752     // Determine how many initial bad blocks preceed block in device.
753     for (i = 0; ndm->init_bad_blk[i] <= bn; ++i)
754         ;
755 
756     // Subtract the preceeding initial bad blocks to obtain the virtual
757     // block number corresponding to the physical block.
758     vbn = bn - i;
759 
760 #if NDM_DEBUG
761     // Check that reverse mapping from physical to virtual block worked
762     // correctly by matching it against the normal mapping.
763     PfAssert((new_bn = get_pbn(ndm, vbn, RD_MAPPING)) != (ui32)-1);
764     PfAssert(new_bn == bn || new_bn >= ndm->frst_reserved);
765 #endif
766 
767     // Loop over partitions to find one this virtual block belongs to.
768     for (i = 0; i < ndm->num_partitions; ++i)
769         if (ndm->partitions[i].first_block <= vbn &&
770             ndm->partitions[i].first_block + ndm->partitions[i].num_blocks > vbn)
771             return ndm->partitions[i].type;
772 
773     // Partition not found. Return 0.
774     return 0;
775 }
776 #endif // INC_FFS_NDM && INC_FTL_NDM
777 
778 #if INC_FFS_NDM || INC_FTL_NDM_MLC || INC_FTL_NDM_SLC
779 //  write_page: Write a page to flash for FFS and FTL
780 //
781 //      Inputs: ndm = pointer to NDM control block
782 //              pn = virtual page number
783 //              buf = pointer to main page buffer array
784 //              spare = pointer to spare page buffer array (1 if FFS)
785 //              action = NDM_NONE, NDM_ECC, or NDM_ECC_VAL
786 //
787 //     Returns: 0 on success, -2 on fatal error
788 //
write_page(NDM ndm,ui32 vpn,const ui8 * data,ui8 * spare,int action)789 static int write_page(NDM ndm, ui32 vpn, const ui8* data, ui8* spare, int action) {
790     ui32 vbn, bn, pn;
791     int rc;
792 
793     // Compute the virtual block number and check for error.
794     vbn = vpn / ndm->pgs_per_blk;
795     if (vbn >= ndm->num_vblks) {
796         FsError(EINVAL);
797         return -2;
798     }
799 
800     // Write page until successful or failure other than bad block.
801     for (;;) {
802         // Get the physical block number from virtual one.
803         bn = get_pbn(ndm, vbn, WR_MAPPING);
804         if (bn == (ui32)-1)
805             return -2;
806 
807         // Compute physical page number.
808         pn = bn * ndm->pgs_per_blk + vpn % ndm->pgs_per_blk;
809 
810         // Call TargetNDM driver to write the page.
811         rc = ndm->write_page(pn, data, spare, action, ndm->dev);
812 
813         // Return 0 if successful.
814         if (rc == 0)
815             return 0;
816 
817         // Else if chip error, mark block bad and do bad block recovery.
818         else if (rc == -1) {
819             if (ndmMarkBadBlock(ndm, pn, WRITE_PAGE))
820                 return -2;
821         }
822 
823         // Else fatal error, return -2.
824         else {
825             PfAssert(rc == -2);
826             FsError(EIO);
827             return -2;
828         }
829     }
830 } //lint !e818
831 #endif // INC_FFS_NDM || INC_FTL_NDM_MLC || INC_FTL_NDM_SLC
832 
833 #if INC_FTL_NDM
834 //   wr_pg_ftl: FTL driver function - write page (data + spare)
835 //
836 //      Inputs: vpn = virtual page number
837 //              data = pointer to buffer containing data to write
838 //              spare = pointer to buffer containing spare bytes
839 //              ndm_ptr = NDM control block handle
840 //
841 //     Returns: 0 on success, -2 on fatal error
842 //
wr_pg_ftl(ui32 vpn,const void * data,void * spare,void * ndm_ptr)843 static int wr_pg_ftl(ui32 vpn, const void* data, void* spare, void* ndm_ptr) {
844     int action, status;
845     NDM ndm = ndm_ptr;
846 
847     // If volume block, just ECC, else request validity checks too.
848     if (RD32_LE(&((ui8*)spare)[5]) == (ui32)-1)
849         action = NDM_ECC;
850     else
851         action = NDM_ECC_VAL;
852 
853     // Grab exclusive access to TargetNDM internals.
854     semPend(ndm->sem, WAIT_FOREVER);
855 
856     // Write page to flash for FTL.
857     status = write_page(ndm, vpn, data, spare, action);
858 
859     // Release exclusive access to NDM and return status.
860     semPostBin(ndm->sem);
861     return status;
862 } //lint !e818
863 
864 // rd_spare_ftl: FTL driver function - read/decode page spare area
865 //
866 //      Inputs: vpn = virtual page number
867 //              spare = buffer to read sparea area into
868 //              ndm_ptr = NDM control block handle
869 //
870 //     Returns: 0 on success, -1 on ECC error, -2 on fatal error, 1 if
871 //              block page belongs to needs to be recycled
872 //
rd_spare_ftl(ui32 vpn,void * spare,void * ndm_ptr)873 static int rd_spare_ftl(ui32 vpn, void* spare, void* ndm_ptr) {
874     ui32 vbn, bn, pn;
875     NDM ndm = ndm_ptr;
876     int status;
877 
878     // Compute virtual block number. Return -2 if out-of-range.
879     vbn = vpn / ndm->pgs_per_blk;
880     if (vbn >= ndm->num_vblks) {
881         FsError(EINVAL);
882         return -2;
883     }
884 
885     // Grab exclusive access to TargetNDM internals.
886     semPend(ndm->sem, WAIT_FOREVER);
887 
888     // Get physical block number from virtual one. Return -2 if error.
889     bn = get_pbn(ndm, vbn, RD_MAPPING);
890     if (bn == (ui32)-1) {
891         semPostBin(ndm->sem);
892         return -2;
893     }
894 
895     // Compute physical page number.
896     pn = bn * ndm->pgs_per_blk + vpn % ndm->pgs_per_blk;
897 
898     // Read and decode spare. Call FsError() if error.
899     status = ndm->read_decode_spare(pn, spare, ndm->dev);
900     if (status < 0)
901         FsError(EIO);
902 
903     // Release exclusive access to NDM and return status.
904     semPostBin(ndm->sem);
905     return status;
906 }
907 
908 // pg_check_ftl: FTL driver function - determine status of a page
909 //
910 //      Inputs: vpn = virtual page number
911 //              data = buffer that will hold page data
912 //              spare = buffer that will hold page spare
913 //              ndm_ptr = NDM control block handle
914 //
915 //     Returns: -1 if error, else NDM_PAGE_ERASED (0), NDM_PAGE_VALID
916 //              (1), or NDM_PAGE_INVALID (2)
917 //
pg_check_ftl(ui32 vpn,ui8 * data,ui8 * spare,void * ndm_ptr)918 static int pg_check_ftl(ui32 vpn, ui8* data, ui8* spare, void* ndm_ptr) {
919     NDM ndm = ndm_ptr;
920     ui32 vbn, bn, pn;
921     int status;
922 
923     // Compute the virtual block number.
924     vbn = vpn / ndm->pgs_per_blk;
925     if (vbn >= ndm->num_vblks)
926         return FsError(EINVAL);
927 
928     // Grab exclusive access to TargetNDM internals.
929     semPend(ndm->sem, WAIT_FOREVER);
930 
931     // Get the physical block number from virtual block number.
932     bn = get_pbn(ndm, vbn, RD_MAPPING);
933     if (bn == (ui32)-1) {
934         semPostBin(ndm->sem);
935         return -1;
936     }
937 
938     // Compute physical page number.
939     pn = bn * ndm->pgs_per_blk + vpn % ndm->pgs_per_blk;
940 
941     // Call the NDM driver to determine page status.
942     if (ndm->check_page(pn, data, spare, &status, ndm->dev)) {
943         semPostBin(ndm->sem);
944         return FsError(EIO);
945     }
946 
947     // Release exclusive access to NDM and return success.
948     semPostBin(ndm->sem);
949     return status;
950 }
951 #endif // INC_FTL_NDM
952 
953 #if INC_FFS_NDM || INC_FTL_NDM
954 //   read_page:  FFS/FTL driver function - read page (data only)
955 //
956 //      Inputs: vpn = virtual page number
957 //              buf = pointer to buffer to copy data to
958 //              ndm_ptr = NDM control block handle
959 //
960 //     Returns: 0 on success, -1 on uncorrectable ECC error, -2 on
961 //              permanent fatal error, 1 if block page belongs to
962 //              needs to be recycled
963 //
read_page(ui32 vpn,void * buf,void * ndm_ptr)964 static int read_page(ui32 vpn, void* buf, void* ndm_ptr) {
965     NDM ndm = ndm_ptr;
966     ui32 vbn, bn, pn;
967     int status;
968 
969     // Compute the virtual block number based on virtual page number.
970     vbn = vpn / ndm->pgs_per_blk;
971     if (vbn >= ndm->num_vblks) {
972         FsError(EINVAL);
973         return -2;
974     }
975 
976     // Grab exclusive access to TargetNDM internals.
977     semPend(ndm->sem, WAIT_FOREVER);
978 
979     // Get the physical block number from virtual one.
980     bn = get_pbn(ndm, vbn, RD_MAPPING);
981     if (bn == (ui32)-1) {
982         semPostBin(ndm->sem);
983         return -2;
984     }
985 
986     // Compute physical page number.
987     pn = bn * ndm->pgs_per_blk + vpn % ndm->pgs_per_blk;
988 
989     // Read decode page data.
990     status = ndm->read_page(pn, buf, ndm->spare_buf, ndm->dev);
991 
992     // Release exclusive access to TargetNDM internals.
993     semPostBin(ndm->sem);
994 
995     // If error, set errno. Return status.
996     if (status) {
997         if (status == -1)
998             FsError(EINVAL);
999         else if (status == -2)
1000             FsError(EIO);
1001     }
1002     return status;
1003 }
1004 
1005 // ftl_xfr_page: FFS/FTL driver function - transfer a page
1006 //
1007 //      Inputs: old_vpn = old virtual page number
1008 //              new_vpn = new virtual page number
1009 //              buf = temporary buffer for swapping main page data
1010 //              spare = buffer holding new page's spare data
1011 //              ndm_ptr = NDM control block handle
1012 //
1013 //     Returns: 0 on success, -2 on fatal error, 1 on ECC decode error
1014 //
ftl_xfr_page(ui32 old_vpn,ui32 new_vpn,ui8 * buf,ui8 * spare,void * ndm_ptr)1015 static int ftl_xfr_page(ui32 old_vpn, ui32 new_vpn, ui8* buf, ui8* spare, void* ndm_ptr) {
1016     ui32 old_vbn, new_vbn, old_bn, new_bn, old_pn, new_pn;
1017     int status, action;
1018     NDM ndm = ndm_ptr;
1019 
1020     // Grab exclusive access to TargetNDM internals.
1021     semPend(ndm->sem, WAIT_FOREVER);
1022 
1023 // Based on spare buffer (NULL for FFS), determine if spare area
1024 // is also to be encoded (FTL only).
1025 #if INC_FFS_NDM && INC_FTL_NDM
1026     if (spare == NULL)
1027 #endif
1028 #if INC_FFS_NDM
1029     {
1030         action = NDM_NONE;
1031         spare = ndm->spare_buf;
1032         WR32_BE(0xFFFFFFFF, &spare[EB_FRST_RESERVED]);
1033     }
1034 #endif
1035 #if INC_FFS_NDM && INC_FTL_NDM
1036     else
1037 #endif
1038 #if INC_FTL_NDM
1039     {
1040         // If FTL valid block counts, do ECC + validity, else ECC only.
1041         if (RD32_LE(&spare[5]) != (ui32)-1)
1042             action = NDM_ECC_VAL;
1043         else
1044             action = NDM_ECC;
1045     }
1046 #endif
1047 
1048     // Compute the old/new virtual block numbers based on virtual pages.
1049     old_vbn = old_vpn / ndm->pgs_per_blk;
1050     new_vbn = new_vpn / ndm->pgs_per_blk;
1051     if (old_vbn >= ndm->num_vblks || new_vbn >= ndm->num_vblks) {
1052         FsError(EINVAL);
1053         semPostBin(ndm->sem);
1054         return -2;
1055     }
1056 
1057     // Get the physical old block number from virtual one.
1058     old_bn = get_pbn(ndm, old_vbn, RD_MAPPING);
1059     if (old_bn == (ui32)-1) {
1060         semPostBin(ndm->sem);
1061         return -2;
1062     }
1063 
1064     // Compute the old physical page number.
1065     old_pn = old_bn * ndm->pgs_per_blk + old_vpn % ndm->pgs_per_blk;
1066 
1067     // Copy page to new block until success or error other than bad block
1068     for (;;) {
1069         // Get the physical new block number for virtual one.
1070         new_bn = get_pbn(ndm, new_vbn, WR_MAPPING);
1071         if (new_bn == (ui32)-1) {
1072             semPostBin(ndm->sem);
1073             return -2;
1074         }
1075 
1076         // Compute the new physical page number.
1077         new_pn = new_bn * ndm->pgs_per_blk + new_vpn % ndm->pgs_per_blk;
1078 
1079         // Transfer data from old page to new page.
1080         status = ndm->xfr_page(old_pn, new_pn, buf, ndm->tmp_spare, spare, action, ndm->dev_ndm);
1081 
1082         // Break if success (0) or ECC decode error (1).
1083         if (status >= 0)
1084             break;
1085 
1086         // Else if fatal error (-2), break to return -2.
1087         if (status == -2) {
1088             FsError(EIO);
1089             break;
1090         }
1091 
1092         // Else chip error (-1), mark block bad and do bad block recovery.
1093         else {
1094             PfAssert(status == -1);
1095             if (ndmMarkBadBlock(ndm, new_pn, WRITE_PAGE)) {
1096                 status = -2;
1097                 break;
1098             }
1099         }
1100     }
1101 
1102     // Release exclusive access to NDM and return status.
1103     semPostBin(ndm->sem);
1104     return status;
1105 } //lint !e818
1106 #endif // INC_FFS_NDM || INC_FTL_NDM
1107 
1108 #if INC_FFS_NDM
1109 //   wr_pg_ffs: FFS driver function - write page (data + spare)
1110 //
1111 //      Inputs: buf = pointer to buffer containing data to write
1112 //              vpn = virtual page number
1113 //              type = page type flag
1114 //              ndm_ptr = NDM control block handle
1115 //
1116 //     Returns: 0 on success, -2 on fatal error
1117 //
wr_pg_ffs(const void * buf,ui32 vpn,ui32 type,void * ndm_ptr)1118 static int wr_pg_ffs(const void* buf, ui32 vpn, ui32 type, void* ndm_ptr) {
1119     int status;
1120     NDM ndm = ndm_ptr;
1121 
1122     // Grab exclusive access to TargetNDM internals.
1123     semPend(ndm->sem, WAIT_FOREVER);
1124 
1125     // Prepare page's spare area.
1126     memset(ndm->spare_buf, 0xFF, ndm->eb_size);
1127     WR32_BE(type, &ndm->spare_buf[EB_FRST_RESERVED]);
1128 
1129     // Write page to flash for FFS.
1130     status = write_page(ndm, vpn, buf, ndm->spare_buf, NDM_NONE);
1131 
1132     // Release exclusive access to NDM and return status.
1133     semPostBin(ndm->sem);
1134     return status;
1135 }
1136 
1137 // rd_type_ffs: FFS driver function - read page type from spare
1138 //
1139 //      Inputs: vpn = virtual page number
1140 //              ndm_ptr = NDM control block handle
1141 //      Output: *type = four byte type value for page
1142 //
1143 //     Returns: 0 on success, -2 on fatal error
1144 //
rd_type_ffs(ui32 vpn,ui32 * type,void * ndm_ptr)1145 static int rd_type_ffs(ui32 vpn, ui32* type, void* ndm_ptr) {
1146     ui32 vbn, bn, pn;
1147     NDM ndm = ndm_ptr;
1148 
1149     // Compute the virtual block number based on virtual page number.
1150     vbn = vpn / ndm->pgs_per_blk;
1151     if (vbn >= ndm->num_vblks) {
1152         FsError(EINVAL);
1153         return -2;
1154     }
1155 
1156     // Grab exclusive access to TargetNDM internals.
1157     semPend(ndm->sem, WAIT_FOREVER);
1158 
1159     // Get the physical block number from virtual one.
1160     bn = get_pbn(ndm, vbn, RD_MAPPING);
1161     if (bn == (ui32)-1) {
1162         semPostBin(ndm->sem);
1163         return -2;
1164     }
1165 
1166     // Compute physical page number.
1167     pn = bn * ndm->pgs_per_blk + vpn % ndm->pgs_per_blk;
1168 
1169     // Read spare. Return -2 if fatal error.
1170     if (ndm->read_spare(pn, ndm->spare_buf, ndm->dev)) {
1171         FsError(EIO);
1172         semPostBin(ndm->sem);
1173         return -2;
1174     }
1175 
1176     // Extract the type from the spare area.
1177     *type = RD32_BE(&ndm->spare_buf[EB_FRST_RESERVED]);
1178 
1179     // Release exclusive access to NDM and return success.
1180     semPostBin(ndm->sem);
1181     return 0;
1182 }
1183 
1184 // pg_blank_ffs: Check if a page is empty - both data and spare
1185 //
1186 //      Inputs: vpn = virtual page number
1187 //              ndm_ptr = NDM control block handle
1188 //
1189 //     Returns: TRUE if erased, FALSE if any 0 bit, -2 if fatal error
1190 //
pg_blank_ffs(ui32 vpn,void * ndm_ptr)1191 static int pg_blank_ffs(ui32 vpn, void* ndm_ptr) {
1192     NDM ndm = ndm_ptr;
1193     ui32 vbn, bn, pn;
1194     int status;
1195 
1196     // Compute the virtual block number based on virtual page number.
1197     vbn = vpn / ndm->pgs_per_blk;
1198     if (vbn >= ndm->num_vblks) {
1199         FsError(EINVAL);
1200         return -2;
1201     }
1202 
1203     // Grab exclusive access to TargetNDM internals.
1204     semPend(ndm->sem, WAIT_FOREVER);
1205 
1206     // Get the physical block number from virtual one.
1207     bn = get_pbn(ndm, vbn, RD_MAPPING);
1208     if (bn == (ui32)-1) {
1209         semPostBin(ndm->sem);
1210         return -2;
1211     }
1212 
1213     // Compute physical page number.
1214     pn = bn * ndm->pgs_per_blk + vpn % ndm->pgs_per_blk;
1215 
1216     // Call driver to see if main and spare areas are erased.
1217     status = ndm->page_blank(pn, ndm->main_buf, ndm->spare_buf, ndm->dev);
1218 
1219     // Release exclusive access to NDM and return status.
1220     semPostBin(ndm->sem);
1221 
1222     // If error, set errno before returning status.
1223     if (status < 0)
1224         FsError(EIO);
1225     return status;
1226 }
1227 #endif // INC_FFS_NDM
1228 
1229 #if INC_FFS_NDM_MLC || INC_FTL_NDM_MLC
1230 // pair_offset: FTL driver function (MLC NAND) - pair offset
1231 //
1232 //      Inputs: page_offset = page offset within block
1233 //              ndm_ptr = NDM control block handle
1234 //
1235 //     Returns: Pair page offset within block if any, page offset
1236 //              otherwise
1237 //
pair_offset(ui32 page_offset,void * ndm_ptr)1238 static ui32 pair_offset(ui32 page_offset, void* ndm_ptr) {
1239     NDM ndm = ndm_ptr;
1240 
1241     return ndm->pair_offset(page_offset, ndm->dev);
1242 }
1243 #endif // INC_FFS_NDM_MLC || INC_FTL_NDM_MLC
1244 
1245 // Global Function Definitions
1246 
1247 // ndmMarkBadBlock: Mark virtual block bad and do bad block recovery
1248 //
1249 //      Inputs: ndm = pointer to NDM control block
1250 //              arg = bad block or page depending on cause
1251 //              cause = ERASE_BLOCK or WRITE_PAGE failure
1252 //
1253 //     Returns: 0 on success, -1 on failure
1254 //
ndmMarkBadBlock(NDM ndm,ui32 arg,ui32 cause)1255 int ndmMarkBadBlock(NDM ndm, ui32 arg, ui32 cause) {
1256     ui32 bad_b, bad_pn, free_b, i, old_pn, new_pn;
1257     int status, transfer_finished;
1258 #if INC_FFS_NDM && INC_FTL_NDM
1259     ui8 part_type;
1260 #endif
1261 
1262     // Clear virtual to physical translation caches.
1263     ndm->last_wr_vbn = ndm->last_rd_vbn = (ui32)-1;
1264 
1265     // Get a free block to replace the bad virtual block.
1266     free_b = get_free_virt_blk(ndm);
1267     if (free_b == (ui32)-1)
1268         return FsError(ENOSPC);
1269 
1270     // Based on cause, figure out bad block and first bad page.
1271     if (cause == ERASE_BLOCK) {
1272         bad_b = arg;
1273         bad_pn = bad_b * ndm->pgs_per_blk;
1274     } else {
1275         bad_pn = arg;
1276         bad_b = bad_pn / ndm->pgs_per_blk;
1277     }
1278 
1279 #if INC_FFS_NDM && INC_FTL_NDM
1280     // Figure out the partition type for bad block.
1281     part_type = get_part_type(ndm, bad_b);
1282 #endif
1283 
1284     // Search the running bad block list for this block.
1285     for (i = 0;; ++i) {
1286         // Check if reached end of list without finding match.
1287         if (i == ndm->num_rbb) {
1288             // Adjust bad block count. If too many, return -1.
1289             if (++ndm->num_bad_blks > ndm->max_bad_blks)
1290                 return FsError(ENOSPC);
1291 
1292             // Add block as last list entry and then break.
1293             ndm->run_bad_blk[ndm->num_rbb].key = bad_b;
1294             ndm->run_bad_blk[ndm->num_rbb].val = free_b;
1295             ++ndm->num_rbb;
1296             break;
1297         }
1298 
1299         // If in list (due to recovery of bad block transfer), update it.
1300         if (ndm->run_bad_blk[i].key == bad_b) {
1301             ndm->run_bad_blk[i].val = free_b;
1302             break;
1303         }
1304     }
1305 
1306     // Loop until bad block recovery is finished.
1307     for (transfer_finished = FALSE;;) {
1308         // Call driver to erase the free block.
1309         status = ndm->erase_block(free_b * ndm->pgs_per_blk, ndm->dev);
1310 
1311         // Check if the block erase succeeded.
1312         if (status == 0) {
1313             // Finished if block copy unneeded.
1314             if (cause == ERASE_BLOCK)
1315                 break;
1316 
1317             // Prepare control information with bad block transfer data.
1318             ndm->xfr_tblk = free_b;
1319             ndm->xfr_fblk = bad_b;
1320             ndm->xfr_bad_po = bad_pn % ndm->pgs_per_blk;
1321 
1322             // Write TargetNDM metadata (includes bad block lists).
1323             if (ndmWrCtrl(ndm))
1324                 return -1;
1325 
1326             // Transfer data from the bad block to the free block.
1327             old_pn = bad_b * ndm->pgs_per_blk;
1328             new_pn = free_b * ndm->pgs_per_blk;
1329             for (i = 0;; ++i, ++old_pn, ++new_pn) {
1330                 int action;
1331 
1332                 // If bad write page reached, transfer finished.
1333                 if (i == ndm->xfr_bad_po) {
1334                     transfer_finished = TRUE;
1335                     break;
1336                 }
1337 
1338                 // If end of block reached, transfer finished.
1339                 if (i >= ndm->pgs_per_blk) {
1340                     transfer_finished = TRUE;
1341                     break;
1342                 }
1343 
1344                 // Call driver to see if main and spare areas are erased.
1345                 status = ndm->page_blank(old_pn, ndm->main_buf, ndm->spare_buf, ndm->dev);
1346 
1347                 // If erased, skip page. Else if I/O error, return -1.
1348                 if (status == TRUE)
1349                     continue;
1350                 else if (status < 0)
1351                     return FsError(EIO);
1352 
1353                 // Read main data. Return -1 if ECC or fatal error.
1354                 status = ndm->read_page(old_pn, ndm->main_buf, ndm->spare_buf, ndm->dev);
1355                 if (status < 0)
1356                     return FsError(EIO);
1357 
1358 // If FTL partition, read old spare data. Return -1 if ECC or
1359 // fatal error.
1360 #if INC_FFS_NDM && INC_FTL_NDM
1361                 if (part_type != FFS_VOL)
1362 #endif
1363 #if INC_FTL_NDM
1364                 {
1365                     status = ndm->read_decode_spare(old_pn, ndm->spare_buf, ndm->dev);
1366                     if (status < 0)
1367                         return FsError(EIO);
1368                 }
1369 #endif
1370 
1371 // Else if FFS partition, read spare without decoding.
1372 #if INC_FFS_NDM && INC_FTL_NDM
1373                 else
1374 #endif
1375 #if INC_FFS_NDM
1376                 {
1377                     status = ndm->read_spare(old_pn, ndm->spare_buf, ndm->dev);
1378                     if (status == -2)
1379                         return FsError(EIO);
1380                 }
1381 #endif
1382 
1383 // If FFS volume, no ECC on spare and no validity check.
1384 #if INC_FFS_NDM && INC_FTL_NDM
1385                 if (part_type == FFS_VOL)
1386 #endif
1387 #if INC_FFS_NDM
1388                     action = NDM_NONE;
1389 #endif
1390 #if INC_FFS_NDM && INC_FTL_NDM
1391                 else
1392 #endif
1393 #if INC_FTL_NDM
1394                     // FTL volume. If volume page, just ECC the spare bytes.
1395                     if (RD32_LE(&ndm->spare_buf[5]) == (ui32)-1)
1396                     action = NDM_ECC;
1397 
1398                 // Else map page, ECC the spare bytes and prep validity check.
1399                 else
1400                     action = NDM_ECC_VAL;
1401 #endif
1402 
1403                 // Write page to new location. Break if error occurs.
1404                 status = ndm->write_page(new_pn, ndm->main_buf, ndm->spare_buf, action, ndm->dev);
1405                 if (status)
1406                     break;
1407             }
1408         }
1409 
1410         // Break if the block transfer is finished.
1411         if (transfer_finished)
1412             break;
1413 
1414         // Return -1 if fatal error.
1415         if (status == -2)
1416             return FsError(EIO);
1417 
1418         // Else bad block. Mark it. Return -1 if error.
1419         else {
1420             PfAssert(status == -1);
1421             if (mark_extra_bblock(ndm, &free_b))
1422                 return -1;
1423         }
1424     }
1425 
1426     // Update control information to clear the bad block transfer state.
1427     ndm->xfr_tblk = (ui32)-1;
1428     if (ndmWrCtrl(ndm))
1429         return -1;
1430 
1431     // Return success.
1432     return 0;
1433 }
1434 
1435 //   ndmWrCtrl: Write NDM control information to flash
1436 //
1437 //       Input: ndm = pointer to NDM control block
1438 //
1439 //     Returns: 0 on success, -1 on error
1440 //
ndmWrCtrl(NDM ndm)1441 int ndmWrCtrl(NDM ndm) {
1442     ui32 ctrl_blk, first_page = ndm->next_ctrl_start;
1443     int status;
1444 
1445     // Keep writing control information until successful.
1446     for (status = 0;;) {
1447         // Check if this is first write to this control block.
1448         if (first_page % ndm->pgs_per_blk == 0) {
1449             // Call driver to erase the block. Return -1 if fatal error.
1450             status = ndm->erase_block(first_page, ndm->dev);
1451             if (status == -2)
1452                 return FsError(EIO);
1453             if (status)
1454                 ctrl_blk = first_page / ndm->pgs_per_blk;
1455         }
1456 
1457         // Check if block erase command succeeded or was unneeded.
1458         if (status == 0) {
1459             // Write the control information. Return -1 if fatal error.
1460             // Outputs number of bad block, if there is a failure.
1461             status = wr_ctrl_info(ndm, first_page, &ctrl_blk);
1462             if (status == -2)
1463                 return -1;
1464 
1465             // Else break if successful.
1466             else if (status == 0)
1467                 break;
1468             PfAssert(status == -1);
1469         }
1470 
1471         // Block failed. Mark it bad and get new control block.
1472         if (mark_ctrl_bblock(ndm, &ctrl_blk))
1473             return -1;
1474         first_page = ctrl_blk * ndm->pgs_per_blk;
1475     }
1476 
1477     // For SLC devices, start next control write immediately after the
1478     // last page in this control information.
1479     first_page = ndm->last_ctrl_page + 1;
1480 
1481 #if INC_NDM_MLC
1482     // For MLC devices, take into account page pair offset so that new
1483     // write can not affect old metadata in case of power off.
1484     if (ndm->dev_type == NDM_MLC)
1485         first_page = ndmPastPrevPair(ndm, first_page);
1486 #endif
1487 
1488     // If start of next write falls outside the current control block,
1489     // move to the other control block.
1490     ctrl_blk = ndm->last_ctrl_page / ndm->pgs_per_blk;
1491     if (first_page / ndm->pgs_per_blk != ctrl_blk) {
1492         if (ctrl_blk == ndm->ctrl_blk0)
1493             ctrl_blk = ndm->ctrl_blk1;
1494         else if (ctrl_blk == ndm->ctrl_blk1)
1495             ctrl_blk = ndm->ctrl_blk0;
1496         else
1497             return FsError(EINVAL);
1498         first_page = ctrl_blk * ndm->pgs_per_blk;
1499     }
1500 
1501 #if RDBACK_CHECK
1502     // Read-back verify the TargetNDM metadata.
1503     ndmCkMeta(ndm);
1504 #endif
1505 
1506     // Save start of next control info write and return success.
1507     ndm->next_ctrl_start = first_page;
1508     return 0;
1509 }
1510 
1511 // ndmUnformat: Unformat (erase all good blocks) an NDM
1512 //
1513 //       Input: ndm = pointer to NDM control block
1514 //
1515 //     Returns: 0 on success, -1 on failure
1516 //
ndmUnformat(NDM ndm)1517 int ndmUnformat(NDM ndm) {
1518     int status;
1519     ui32 b;
1520 
1521     // Grab exclusive access to the NDM.
1522     semPend(ndm->sem, WAIT_FOREVER);
1523 
1524 #if NV_NDM_CTRL_STORE
1525     // Invalidate the saved first page of NDM control information.
1526     NvNdmCtrlPgWr(0);
1527 #endif
1528 
1529     // Walk through all the blocks, erasing good ones.
1530     for (b = 0; b < ndm->num_dev_blks; ++b) {
1531         // If block is an initial bad block, skip it.
1532         if (ndmInitBadBlock(ndm, b))
1533             continue;
1534 
1535         // If block is a running bad block, skip it.
1536         if (run_bad_block(ndm, b))
1537             continue;
1538 
1539         // Erase block. If command fails, mark as bad block.
1540         status = ndm->erase_block(b * ndm->pgs_per_blk, ndm->dev);
1541         if (status == -1) {
1542             if (ndmMarkBadBlock(ndm, b, ERASE_BLOCK)) {
1543                 semPostBin(ndm->sem);
1544                 return -1;
1545             }
1546         }
1547 
1548         // If failure other than bad block, return -1.
1549         else if (status) {
1550             semPostBin(ndm->sem);
1551             return FsError(EIO);
1552         }
1553     }
1554 
1555     // Remove all the volumes on the device.
1556     status = ndmDelVols(ndm);
1557 
1558     // Release exclusive access to the NDM and return status.
1559     semPostBin(ndm->sem);
1560     return status;
1561 } //lint !e818
1562 
1563 // ndmGetNumVBlocks: Return number of virtual blocks in NDM
1564 //
1565 //       Input: ndm = pointer to NDM control block
1566 //
ndmGetNumVBlocks(CNDM ndm)1567 ui32 ndmGetNumVBlocks(CNDM ndm) {
1568     return ndm->num_vblks;
1569 }
1570 
1571 #if INC_FFS_NDM
1572 // ndmAddVolFFS: Add an FFS volume based on an NDM partition
1573 //
1574 //      Inputs: ndm = pointer to NDM control block
1575 //              part_num = NDM partition number
1576 //              ffs_dvr = FFS driver information
1577 //
1578 //     Returns: 0 on success, -1 on error
1579 //
ndmAddVolFFS(NDM ndm,ui32 part_num,FfsVol * ffs_dvr)1580 int ndmAddVolFFS(NDM ndm, ui32 part_num, FfsVol* ffs_dvr) {
1581     NDMPartition* part;
1582 
1583     // Check partition number.
1584     if (part_num >= ndm->num_partitions)
1585         return FsError(EINVAL);
1586     part = &ndm->partitions[part_num];
1587 
1588     // Check partition type.
1589     if (part->type != FFS_VOL)
1590         return FsError(EINVAL);
1591 
1592     // Check partition first block and number of blocks.
1593     if (part->first_block + part->num_blocks > ndm->num_vblks)
1594         return FsError(ENOSPC);
1595 
1596     // Set up the non-customizable part of the FFS driver.
1597     ffs_dvr->name = part->name;
1598     ffs_dvr->num_blocks = part->num_blocks;
1599     ffs_dvr->block_size = ndm->block_size;
1600     ffs_dvr->page_size = ndm->page_size;
1601     ffs_dvr->driver.nand.start_page = part->first_block * ndm->pgs_per_blk;
1602     ffs_dvr->driver.nand.write_page = wr_pg_ffs;
1603     ffs_dvr->driver.nand.write_pages = ndmWritePages;
1604     ffs_dvr->driver.nand.transfer_page = ftl_xfr_page;
1605     ffs_dvr->driver.nand.read_page = read_page;
1606     ffs_dvr->driver.nand.read_pages = ndmReadPages;
1607     ffs_dvr->driver.nand.read_type = rd_type_ffs;
1608     ffs_dvr->driver.nand.page_erased = pg_blank_ffs;
1609     ffs_dvr->driver.nand.erase_block = ndmEraseBlock;
1610     ffs_dvr->flags |= FSF_TRANSFER_PAGE | FSF_MULTI_ACCESS;
1611     ffs_dvr->vol = ndm;
1612 
1613 // Set volume type and type-specific driver routines.
1614 #if INC_FFS_NDM_SLC && INC_FFS_NDM_MLC
1615     if (ndm->dev_type == NDM_SLC)
1616 #endif
1617 #if INC_FFS_NDM_SLC
1618     {
1619         ffs_dvr->type = FFS_NAND_SLC;
1620     }
1621 #endif
1622 #if INC_FFS_NDM_SLC && INC_FFS_NDM_MLC
1623     else
1624 #endif
1625 #if INC_FFS_NDM_MLC
1626     {
1627         ffs_dvr->type = FFS_NAND_MLC;
1628         ffs_dvr->driver.nand.pair_offset = pair_offset;
1629     }
1630 #endif
1631 
1632     // Create FFS volume.
1633     return FfsAddNdmVol(ffs_dvr, ndm->spare_buf);
1634 }
1635 #endif // INC_FFS_NDM
1636 
1637 #if INC_FTL_NDM
1638 #if INC_SECT_FTL
1639 // ndmAddFatFTL: Add a TargetFAT FTL to an NDM partition
1640 //
1641 //      Inputs: ndm = pointer to NDM control block
1642 //              part_num = NDM partition number
1643 //              ftl = FTL driver information
1644 //              fat = FAT volume information
1645 //
1646 //     Returns: Pointer to FTL control block on success, else NULL
1647 //
ndmAddFatFTL(NDM ndm,ui32 part_num,FtlNdmVol * ftl,FatVol * fat)1648 void* ndmAddFatFTL(NDM ndm, ui32 part_num, FtlNdmVol* ftl, FatVol* fat) {
1649     NDMPartition* part;
1650 
1651     // Check partition number.
1652     if (part_num >= ndm->num_partitions) {
1653         FsError(EINVAL);
1654         return NULL;
1655     }
1656     part = &ndm->partitions[part_num];
1657 
1658     // Check partition type.
1659     if (part->type != FAT_VOL) {
1660         FsError(EINVAL);
1661         return NULL;
1662     }
1663 
1664     // Check partition first block and number of blocks.
1665     if (part->first_block + part->num_blocks > ndm->num_vblks) {
1666         FsError(ENOSPC);
1667         return NULL;
1668     }
1669 
1670     // Set up the non-customizable part of the FTL/FAT drivers.
1671     ftl->ndm = ndm;
1672     ftl->start_page = part->first_block * ndm->pgs_per_blk;
1673     ftl->num_blocks = part->num_blocks;
1674     ftl->block_size = ndm->block_size;
1675     ftl->page_size = ndm->page_size;
1676     ftl->eb_size = ndm->eb_size;
1677     ftl->erase_block = ndmEraseBlock;
1678     ftl->write_data_and_spare = wr_pg_ftl;
1679     ftl->write_pages = ndmWritePages;
1680     ftl->read_spare = rd_spare_ftl;
1681     ftl->read_pages = ndmReadPages;
1682     ftl->page_check = pg_check_ftl;
1683     ftl->transfer_page = ftl_xfr_page;
1684 #if INC_FTL_NDM_MLC
1685     ftl->pair_offset = pair_offset;
1686 #endif
1687     ftl->type = ndm->dev_type;
1688     fat->name = part->name;
1689 
1690     // Create a new TargetFTL-NDM FAT FTL for this partition.
1691     return FtlNdmAddFatFTL(ftl, fat);
1692 }
1693 
1694 // ndmAddVolFatFTL: Add FTL and FAT volume to NDM partition
1695 //
1696 //      Inputs: ndm = pointer to NDM control block
1697 //              part_num = NDM partition number
1698 //              ftl = FTL driver information
1699 //              fat = FAT volume information
1700 //
1701 //     Returns: 0 on success, -1 on error
1702 //
ndmAddVolFatFTL(NDM ndm,ui32 part_num,FtlNdmVol * ftl,FatVol * fat)1703 int ndmAddVolFatFTL(NDM ndm, ui32 part_num, FtlNdmVol* ftl, FatVol* fat) {
1704     void* ftl_ndm;
1705 
1706     // Add a TargetFAT FTL to this TargetNDM partition.
1707     ftl_ndm = ndmAddFatFTL(ndm, part_num, ftl, fat);
1708     if (ftl_ndm == NULL)
1709         return -1;
1710 
1711     // Register FAT volume on top of FTL. Delete FTL if error.
1712     if (FatAddVol(fat)) {
1713         FtlnFreeFTL(ftl_ndm);
1714         return -1;
1715     }
1716 
1717     // Return success.
1718     return 0;
1719 }
1720 #endif // INC_SECT_FTL
1721 
1722 #if INC_PAGE_FTL
1723 // ndmAddVolXfsFTL: Add FTL and XFS volume to NDM partition
1724 //
1725 //      Inputs: ndm = pointer to NDM control block
1726 //              part_num = NDM partition number
1727 //              ftl = FTL driver information
1728 //              xfs = XFS volume information
1729 //
1730 //     Returns: 0 on success, -1 on error
1731 //
ndmAddVolXfsFTL(NDM ndm,ui32 part_num,FtlNdmVol * ftl,XfsVol * xfs)1732 int ndmAddVolXfsFTL(NDM ndm, ui32 part_num, FtlNdmVol* ftl, XfsVol* xfs) {
1733     NDMPartition* part;
1734     void* ftl_ndm;
1735 
1736     // Check partition number.
1737     if (part_num >= ndm->num_partitions)
1738         return FsError(EINVAL);
1739     part = &ndm->partitions[part_num];
1740 
1741     // Check partition type.
1742     if (part->type != XFS_VOL)
1743         return FsError(EINVAL);
1744 
1745     // Check partition first block and number of blocks.
1746     if (part->first_block + part->num_blocks > ndm->num_vblks)
1747         return FsError(ENOSPC);
1748 
1749     // Set up the non-customizable part of the FTL/XFS drivers.
1750     ftl->ndm = ndm;
1751     ftl->start_page = part->first_block * ndm->pgs_per_blk;
1752     ftl->num_blocks = part->num_blocks;
1753     ftl->block_size = ndm->block_size;
1754     ftl->page_size = ndm->page_size;
1755     ftl->eb_size = ndm->eb_size;
1756     ftl->erase_block = ndmEraseBlock;
1757     ftl->write_data_and_spare = wr_pg_ftl;
1758     ftl->write_pages = ndmWritePages;
1759     ftl->read_spare = rd_spare_ftl;
1760     ftl->read_pages = ndmReadPages;
1761     ftl->page_check = pg_check_ftl;
1762     ftl->transfer_page = ftl_xfr_page;
1763 #if INC_FTL_NDM_MLC
1764     ftl->pair_offset = pair_offset;
1765 #endif
1766     ftl->type = ndm->dev_type;
1767     xfs->name = part->name;
1768 
1769     // Add a TargetXFS FTL to this TargetNDM partition.
1770     ftl_ndm = FtlNdmAddXfsFTL(ftl, xfs);
1771     if (ftl_ndm == NULL)
1772         return -1;
1773 
1774     // Register XFS volume on top of FTL. Delete FTL if error.
1775     if (XfsAddVol(xfs)) {
1776         FtlnFreeFTL(ftl_ndm);
1777         return -1;
1778     }
1779 
1780     // Return success.
1781     return 0;
1782 }
1783 #endif // INC_PAGE_FTL
1784 #endif // INC_FTL_NDM_MLC || INC_FTL_NDM_SLC
1785 
1786 #if INC_FFS_NDM || INC_FTL_NDM_MLC || INC_FTL_NDM_SLC
1787 // ndmReadPages: FFS/FTL driver function - read multiple consecutive
1788 //              pages from a single block (data only)
1789 //
1790 //      Inputs: vpn = starting virtual page number
1791 //              count = number of consecutive virtual pages to read
1792 //              data = pointer to buffer to copy main page data to
1793 //              spare = points to array of page spare data sets
1794 //              ndm_ptr = NDM control block handle
1795 //
1796 //     Returns: -2 on fatal error, -1 on error, 0 on success, 1 if
1797 //              block needs to be recycled
1798 //
ndmReadPages(ui32 vpn,ui32 count,void * data,void * spare,void * ndm_ptr)1799 int ndmReadPages(ui32 vpn, ui32 count, void* data, void* spare, void* ndm_ptr) {
1800     NDM ndm = ndm_ptr;
1801     int status;
1802 
1803     // If NDM driver supplies read_pages(), use it.
1804     if (ndm->read_pages) {
1805         ui32 vbn, bn, pn;
1806 
1807         // Compute the virtual block number based on virtual page number.
1808         vbn = vpn / ndm->pgs_per_blk;
1809         if (vbn >= ndm->num_vblks) {
1810             FsError(EINVAL);
1811             return -2;
1812         }
1813 
1814         // Grab exclusive access to TargetNDM internals.
1815         semPend(ndm->sem, WAIT_FOREVER);
1816 
1817         // Get the physical block number from virtual one.
1818         bn = get_pbn(ndm, vbn, RD_MAPPING);
1819         if (bn == (ui32)-1) {
1820             semPostBin(ndm->sem);
1821             return -2;
1822         }
1823 
1824         // Compute starting physical page number.
1825         pn = bn * ndm->pgs_per_blk + vpn % ndm->pgs_per_blk;
1826 
1827         // Read pages.
1828         status = ndm->read_pages(pn, count, data, spare, ndm->dev);
1829 
1830         // Release exclusive access to NDM and return status.
1831         semPostBin(ndm->sem);
1832         return status;
1833     }
1834 
1835     // Else loop over all pages, reading one at a time.
1836     else {
1837         ui32 i;
1838         int rd_status;
1839         ui8* buf = data;
1840 
1841         // Loop over virtual pages.
1842         for (status = 0, i = 0; i < count; ++i, ++vpn, buf += ndm->page_size) {
1843             // Issue current page read request.
1844             rd_status = read_page(vpn, buf, ndm_ptr);
1845 
1846             // If error, return.
1847             if (rd_status < 0)
1848                 return rd_status;
1849 
1850             // If recycles needed for block, set return status.
1851             if (rd_status == 1)
1852                 status = 1;
1853         }
1854 
1855         // Return status.
1856         return status;
1857     }
1858 }
1859 
1860 // ndmWritePages: FFS/FTL driver function - write multiple consecutive
1861 //              pages to a single block (data only)
1862 //
1863 //      Inputs: vpn = starting virtual page number
1864 //              count = number of consecutive virtual pages to write
1865 //              data = points to array of page main data sets
1866 //              spare = points to array of page spare data sets
1867 //              ndm_ptr = NDM control block handle
1868 //
1869 //     Returns: -1 on error, 0 on success
1870 //
ndmWritePages(ui32 vpn,ui32 count,const void * data,void * spare,void * ndm_ptr)1871 int ndmWritePages(ui32 vpn, ui32 count, const void* data, void* spare, void* ndm_ptr) {
1872     int action, rc = 0;
1873     NDM ndm = ndm_ptr;
1874 
1875     // Ensure all writes are to the same virtual block.
1876     PfAssert(count);
1877     PfAssert(vpn / ndm->pgs_per_blk == (vpn + count - 1) / ndm->pgs_per_blk);
1878 
1879 #if INC_FFS_NDM && INC_FTL_NDM
1880     // If write triggerred by FFS, no extra action required on spare.
1881     if (spare == NULL)
1882 #endif
1883 #if INC_FFS_NDM
1884         action = NDM_NONE;
1885 #endif
1886 
1887 #if INC_FFS_NDM && INC_FTL_NDM
1888     else
1889 #endif
1890 
1891 #if INC_FTL_NDM
1892         // Else for FTL, prepare ECC and, if map page, validity checks too.
1893         if (RD32_LE(&((ui8*)spare)[5]) == (ui32)-1)
1894         action = NDM_ECC;
1895     else
1896         action = NDM_ECC_VAL;
1897 #endif
1898 
1899     // Grab exclusive access to TargetNDM internals.
1900     semPend(ndm->sem, WAIT_FOREVER);
1901 
1902     // If NDM driver supplies write_pages(), use it.
1903     if (ndm->write_pages) {
1904         ui32 vbn;
1905 
1906         // Compute the virtual block number based on virtual page number.
1907         vbn = vpn / ndm->pgs_per_blk;
1908         if (vbn >= ndm->num_vblks) {
1909             semPostBin(ndm->sem);
1910             return FsError(EINVAL);
1911         }
1912 
1913         // Writing to flash until success or failure other than bad block.
1914         for (;;) {
1915             ui32 bn, pn;
1916 
1917             // Get the physical block number from virtual one.
1918             bn = get_pbn(ndm, vbn, WR_MAPPING);
1919             if (bn == (ui32)-1) {
1920                 rc = -1;
1921                 break;
1922             }
1923 
1924             // Compute starting physical page number.
1925             pn = bn * ndm->pgs_per_blk + vpn % ndm->pgs_per_blk;
1926 
1927             // Write pages. If successful, break from loop.
1928             rc = ndm->write_pages(pn, count, data, spare, action, ndm->dev);
1929             if (rc == 0)
1930                 break;
1931 
1932             // If fatal error, break to return -1.
1933             if (rc == -2) {
1934                 rc = FsError(EIO);
1935                 break;
1936             }
1937 
1938             // Else bad block, mark it bad. If error, break to return -1.
1939             else {
1940                 PfAssert(rc == -1);
1941                 if (ndmMarkBadBlock(ndm, pn, WRITE_PAGE))
1942                     break;
1943             }
1944         }
1945     }
1946 
1947     // Else loop over all pages, writing one at a time.
1948     else {
1949         ui32 past = vpn + count;
1950         const ui8* curr_data = data;
1951         ui8* curr_spare = spare;
1952 
1953         // Loop over virtual pages.
1954         for (; vpn < past; ++vpn) {
1955             // Write current page.
1956             rc = write_page(ndm, vpn, curr_data, curr_spare, action);
1957             if (rc)
1958                 break;
1959 
1960             // Advance data pointer and - if not TargetFFS - spare pointer.
1961             curr_data += ndm->page_size;
1962 #if INC_FFS_NDM
1963             if (action != NDM_NONE)
1964 #endif
1965                 curr_spare += ndm->eb_size;
1966         }
1967     }
1968 
1969     // Release exclusive access to NDM and return status.
1970     semPostBin(ndm->sem);
1971     return rc;
1972 }
1973 #endif // INC_FFS_NDM || INC_FTL_NDM_MLC || INC_FTL_NDM_SLC
1974 
1975 // ndmGetNumPartitions: Retrieve number of current partitions in table
1976 //
1977 //       Input: ndm = pointer to NDM control block
1978 //
1979 //     Returns: Current number of partitions in NDM
1980 //
ndmGetNumPartitions(CNDM ndm)1981 ui32 ndmGetNumPartitions(CNDM ndm) {
1982     return ndm->num_partitions;
1983 }
1984 
1985 // ndmSetNumPartitions: Set number of current partitions in table
1986 //
1987 //      Inputs: ndm = pointer to NDM control block
1988 //              num_partitions = number of desired partitions
1989 //
1990 //     Returns: 0 on success, -1 on failure
1991 //
ndmSetNumPartitions(NDM ndm,ui32 num_partitions)1992 int ndmSetNumPartitions(NDM ndm, ui32 num_partitions) {
1993     NDMPartition* new_partitions;
1994 
1995     // If the number of partitions is unchanged, simply return success.
1996     if (num_partitions == ndm->num_partitions)
1997         return 0;
1998 
1999     // If number of partitions is 0, this is a delete table call.
2000     if (num_partitions == 0) {
2001         ndmDeletePartitionTable(ndm);
2002         return 0;
2003     }
2004 
2005     // Allocate space for the new partitions table.
2006     new_partitions = FsCalloc(num_partitions, sizeof(NDMPartition));
2007     if (new_partitions == NULL)
2008         return -1;
2009 
2010     // If there is a current partitions table, copy as much of it as
2011     // possible to the new table before removing it.
2012     if (ndm->partitions) {
2013         ui32 i, max_i = MIN(num_partitions, ndm->num_partitions);
2014 
2015         for (i = 0; i < max_i; ++i)
2016             memcpy(&new_partitions[i], &ndm->partitions[i], sizeof(NDMPartition));
2017         FsFree(ndm->partitions);
2018     }
2019 
2020     // Set the new partition information in the NDM.
2021     ndm->partitions = new_partitions;
2022     ndm->num_partitions = num_partitions;
2023 
2024     // Return success.
2025     return 0;
2026 }
2027 
2028 // ndmGetPartition: Return partition entry handle
2029 //
2030 //      Inputs: ndm = pointer to NDM control block
2031 //              part_num = partition number
2032 //
2033 //     Returns: partition handle on success, NULL on error
2034 //
ndmGetPartition(CNDM ndm,ui32 part_num)2035 const NDMPartition* ndmGetPartition(CNDM ndm, ui32 part_num) {
2036     // If partition number out of bounds, error.
2037     if (part_num >= ndm->num_partitions) {
2038         FsError(EINVAL);
2039         return NULL;
2040     }
2041 
2042     // Return pointer to specified partition structure.
2043     return &ndm->partitions[part_num];
2044 }
2045 
2046 // ndmWritePartition: Write a partition entry into partitions table
2047 //
2048 //      Inputs: ndm = pointer to NDM control block
2049 //              part = buffer to get partition information from
2050 //              part_num = partition number
2051 //              name = partition name
2052 //
2053 //     Returns: 0 on success, -1 on error
2054 //
ndmWritePartition(NDM ndm,const NDMPartition * part,ui32 part_num,const char * name)2055 int ndmWritePartition(NDM ndm, const NDMPartition* part, ui32 part_num, const char* name) {
2056     ui32 i;
2057 
2058     // Ensure name is not too long and copy it to partition structure.
2059     if (strlen(name) >= NDM_PART_NAME_LEN)
2060         return FsError(EINVAL);
2061     strcpy((char*)part->name, name);
2062 
2063     // If partition first block or number of blocks invalid, error.
2064     if (part->first_block >= ndm->num_vblks ||
2065         part->first_block + part->num_blocks > ndm->num_vblks)
2066         return FsError(EINVAL);
2067 
2068     // If there are partitions already, check there is no overlap.
2069     for (i = 0; i < ndm->num_partitions; ++i) {
2070         // Skip partition we want to replace.
2071         if (i == part_num)
2072             continue;
2073 
2074         // Check for overlap only against valid partition entries.
2075         if (ndm->partitions[i].type) {
2076             if ((part->first_block >= ndm->partitions[i].first_block &&
2077                  part->first_block <
2078                      ndm->partitions[i].first_block + ndm->partitions[i].num_blocks) ||
2079                 (ndm->partitions[i].first_block >= part->first_block &&
2080                  ndm->partitions[i].first_block < part->first_block + part->num_blocks))
2081                 return FsError(EINVAL);
2082         }
2083     }
2084 
2085     // If partition number out of bounds, adjust partition table.
2086     if (part_num >= ndm->num_partitions)
2087         if (ndmSetNumPartitions(ndm, part_num + 1))
2088             return -1;
2089 
2090     // Write partition information and return success.
2091     memcpy(&ndm->partitions[part_num], part, sizeof(NDMPartition));
2092     return 0;
2093 }
2094 
2095 // ndmEraseBlock: Erase a block
2096 //
2097 //      Inputs: vpn = base virtual page for block to be erased
2098 //              ndm_ptr = NDM control block handle
2099 //
2100 //     Returns: 0 on success, -1 on fatal error
2101 //
ndmEraseBlock(ui32 vpn,void * ndm_ptr)2102 int ndmEraseBlock(ui32 vpn, void* ndm_ptr) {
2103     NDM ndm = ndm_ptr;
2104     ui32 vbn, bn, pn;
2105     int status;
2106 
2107     // Compute the virtual block number based on virtual page number.
2108     vbn = vpn / ndm->pgs_per_blk;
2109     if (vbn >= ndm->num_vblks)
2110         return FsError(EINVAL);
2111 
2112     // Grab exclusive access to TargetNDM internals.
2113     semPend(ndm->sem, WAIT_FOREVER);
2114 
2115     // Get the physical block number from virtual one.
2116     bn = get_pbn(ndm, vbn, WR_MAPPING);
2117     if (bn == (ui32)-1) {
2118         semPostBin(ndm->sem);
2119         return -1;
2120     }
2121 
2122     // Compute physical page number.
2123     pn = bn * ndm->pgs_per_blk + vpn % ndm->pgs_per_blk;
2124 
2125     // Erase the block.
2126     status = ndm->erase_block(pn, ndm->dev);
2127     if (status < 0) {
2128         // If chip error, do bad block recovery. Else return fatal error.
2129         if (status == -1)
2130             status = ndmMarkBadBlock(ndm, bn, ERASE_BLOCK);
2131         else
2132             FsError(EIO);
2133     }
2134 
2135     // Release exclusive NDM access and return status.
2136     semPostBin(ndm->sem);
2137     return status;
2138 }
2139 
2140 // ndmErasePartition: Erase all the virtual blocks in a partition
2141 //
2142 //      Inputs: ndm = pointer to NDM control block
2143 //              part_num = partition number in table
2144 //
2145 //     Returns: 0 on success, -1 on error
2146 //
ndmErasePartition(NDM ndm,ui32 part_num)2147 int ndmErasePartition(NDM ndm, ui32 part_num) {
2148     ui32 i, vpn;
2149 
2150     // If invalid partition number, ignore call. Return error.
2151     if (part_num >= ndm->num_partitions)
2152         return FsError(EINVAL);
2153 
2154     // Compute virtual page for first page on first block in partition.
2155     vpn = ndm->partitions[part_num].first_block * ndm->pgs_per_blk;
2156 
2157     // Loop over all virtual blocks in partition, erasing each.
2158     for (i = 0; i < ndm->partitions[part_num].num_blocks; ++i, vpn += ndm->pgs_per_blk)
2159         if (ndmEraseBlock(vpn, ndm))
2160             return -1;
2161 
2162     // Return success.
2163     return 0;
2164 }
2165 
2166 // ndmDeletePartition: Clear a partition entry from partition table
2167 //
2168 //      Inputs: ndm = pointer to NDM control block
2169 //              part_num = partition number in table
2170 //
ndmDeletePartition(CNDM ndm,ui32 part_num)2171 void ndmDeletePartition(CNDM ndm, ui32 part_num) {
2172     // If invalid partition number, ignore.
2173     if (part_num >= ndm->num_partitions) {
2174         FsError(EINVAL);
2175         return;
2176     }
2177 
2178     // Clear the partition information in the table for this entry.
2179     memset(&ndm->partitions[part_num], 0, sizeof(NDMPartition));
2180 }
2181 
2182 // ndmDeletePartitionTable: Delete partition table
2183 //
2184 //       Input: ndm = pointer to NDM control block
2185 //
ndmDeletePartitionTable(NDM ndm)2186 void ndmDeletePartitionTable(NDM ndm) {
2187     // If no partitions, simply return.
2188     if (ndm->num_partitions == 0)
2189         return;
2190 
2191     // Remove partition table.
2192     FsFreeClear(&ndm->partitions);
2193     ndm->num_partitions = 0;
2194 }
2195 
2196 // ndmSavePartitionTable: Save partition table to flash
2197 //
2198 //       Input: ndm = pointer to NDM control block
2199 //
2200 //     Returns: 0 on success, -1 on error
2201 //
ndmSavePartitionTable(NDM ndm)2202 int ndmSavePartitionTable(NDM ndm) {
2203     ndm->xfr_tblk = (ui32)-1;
2204     return ndmWrCtrl(ndm);
2205 } //lint !e818
2206 
2207 #if BBL_INSERT_INC
2208 // ndmExtractBBL: Save running bad block list count and data
2209 //
2210 //       Input: ndm = pointer to NDM control block
2211 //
2212 //     Returns: -1 if error, else number of running bad blocks
2213 //
ndmExtractBBL(NDM ndm)2214 int ndmExtractBBL(NDM ndm) {
2215     uint size;
2216     Pair *pair, *tmp, *last_pair;
2217 
2218     // Save running bad block count. Return if there are none.
2219     ExtractedCnt = ndm->num_rbb;
2220     if (ExtractedCnt == 0)
2221         return 0;
2222 
2223     // Allocate memory for running bad block list and copy list to it.
2224     size = sizeof(Pair) * ExtractedCnt;
2225     ExtractedList = malloc(size);
2226     if (ExtractedList == NULL)
2227         return -1;
2228     memcpy(ExtractedList, ndm->run_bad_blk, size);
2229 #if BBL_INSERT_DEBUG
2230     show_rbbl(ndm->run_bad_blk, ndm->num_rbb);
2231     show_rbbl(ExtractedList, ExtractedCnt);
2232 #endif
2233 
2234     // Simplify list, eliminating chains.
2235     pair = ExtractedList;
2236     for (last_pair = pair + ExtractedCnt - 1; pair < last_pair; ++pair) {
2237         if (pair->val != (ui32)-1) {
2238             for (tmp = pair + 1; tmp <= last_pair; ++tmp) {
2239                 if (tmp->key == pair->val) {
2240                     pair->val = tmp->val;
2241                     tmp->val = (ui32)-1;
2242                 }
2243             }
2244         }
2245     }
2246 #if BBL_INSERT_DEBUG
2247     show_rbbl(ExtractedList, ExtractedCnt);
2248 #endif
2249 
2250     // Return number of blocks that failed so far.
2251     return ExtractedCnt;
2252 }
2253 
2254 // ndmInsertBBL: Import saved running bad block list
2255 //
2256 //       Input: ndm = pointer to NDM control block
2257 //
2258 //     Returns: 0 if successful, else -1 on error
2259 //
ndmInsertBBL(NDM ndm)2260 int ndmInsertBBL(NDM ndm) {
2261     ui32 free_b, old_pn, new_pn, past_end;
2262     Pair *last_pair, *pair;
2263     int action, rc;
2264 
2265     // Just return 0 if there were no running bad blocks.
2266     if (ExtractedCnt == 0)
2267         return 0;
2268 
2269     // Loop to recover each bad block.
2270     pair = ExtractedList;
2271     for (last_pair = pair + ExtractedCnt - 1; pair <= last_pair; ++pair) {
2272 #if BBL_INSERT_DEBUG
2273         printf("pair %u: vblk/key=%u, pblk/val=%d\n", pair - ExtractedList, pair->key, pair->val);
2274 #endif
2275 
2276         // Adjust bad block count. If too many, error.
2277         if (++ndm->num_bad_blks > ndm->max_bad_blks)
2278             return FsError(ENOSPC);
2279 
2280         // Get a free block to replace the bad virtual block.
2281         free_b = get_free_virt_blk(ndm);
2282         if (free_b == (ui32)-1)
2283             return FsError(ENOSPC);
2284 
2285         // If physical block assigned, copy its contents to free_b.
2286         if (pair->val != (ui32)-1) {
2287             // Keep looping until successful.
2288             for (;;) {
2289                 // Call driver to erase the free block.
2290                 rc = ndm->erase_block(free_b * ndm->pgs_per_blk, ndm->dev);
2291                 if (rc == -2)
2292                     return FsError(EIO);
2293 
2294                 // Check if block erase command succeeded.
2295                 if (rc == 0) {
2296                     // Transfer data from old pool block to new pool block.
2297                     old_pn = pair->val * ndm->pgs_per_blk;
2298                     new_pn = free_b * ndm->pgs_per_blk;
2299                     past_end = new_pn + ndm->pgs_per_blk;
2300                     for (; new_pn < past_end; ++old_pn, ++new_pn) {
2301                         // Call driver to see if main and spare areas are erased.
2302                         rc = ndm->page_blank(old_pn, ndm->main_buf, ndm->spare_buf, ndm->dev);
2303 
2304                         // If erased, skip page. Else if I/O error, return -1.
2305                         if (rc == TRUE)
2306                             continue;
2307                         else if (rc < 0)
2308                             return FsError(EIO);
2309 
2310                         // Read main data. Return -1 if ECC or fatal error.
2311                         rc = ndm->read_page(old_pn, ndm->main_buf, ndm->spare_buf, ndm->dev);
2312                         if (rc < 0)
2313                             return FsError(EIO);
2314 
2315                         // Read old spare data. Return -1 if ECC or fatal error.
2316                         rc = ndm->read_decode_spare(old_pn, ndm->spare_buf, ndm->dev);
2317                         if (rc < 0)
2318                             return FsError(EIO);
2319 
2320                         // If volume page, request just spare bytes ECC.
2321                         if (RD32_LE(&ndm->spare_buf[5]) == (ui32)-1)
2322                             action = NDM_ECC;
2323 
2324                         // Else map page, request spare ECC/validity check prep.
2325                         else
2326                             action = NDM_ECC_VAL;
2327 
2328                         // Write page. Return -1 if fatal err. Break if block bad.
2329                         rc = ndm->write_page(new_pn, ndm->main_buf, ndm->spare_buf, action,
2330                                              ndm->dev);
2331                         if (rc == -2)
2332                             return -1;
2333                         else if (rc)
2334                             break;
2335                     }
2336 
2337                     // Break if block copy loop completed successfully.
2338                     if (new_pn == past_end)
2339                         break;
2340                 }
2341 
2342                 // Adjust bad block count. If too many, error.
2343                 if (++ndm->num_bad_blks > ndm->max_bad_blks)
2344                     return FsError(ENOSPC);
2345 
2346                 // Add the failed copy block to the running bad block list.
2347                 ndm->run_bad_blk[ndm->num_rbb].val = (ui32)-1;
2348                 ndm->run_bad_blk[ndm->num_rbb].key = free_b;
2349                 ++ndm->num_rbb;
2350 
2351                 // Get a free block to replace the bad virtual block.
2352                 free_b = get_free_virt_blk(ndm);
2353                 if (free_b == (ui32)-1)
2354                     return FsError(ENOSPC);
2355             }
2356         }
2357 
2358         // Add this extracted pair to the current running bad block list.
2359         ndm->run_bad_blk[ndm->num_rbb].val = free_b;
2360         ndm->run_bad_blk[ndm->num_rbb].key = pair->key;
2361         ++ndm->num_rbb;
2362     }
2363 #if BBL_INSERT_DEBUG
2364     show_rbbl(ndm->run_bad_blk, ndm->num_rbb);
2365 #endif
2366 
2367     // Free bad block list memory, zero count, and return success.
2368     free(ExtractedList);
2369     ExtractedCnt = 0;
2370     return 0;
2371 }
2372 #endif // BBL_INSERT_INC
2373 
2374 #if INC_NDM_MLC
2375 // ndmPastPrevPair: Starting at specified page number, find first page
2376 //              that has no earlier paired page
2377 //
2378 //      Inputs: ndm = pointer to NDM control block
2379 //              pn = number of prospective next free page
2380 //
2381 //     Returns: first of next pages whose write failures that can't
2382 //              corrupt previously written pages on same block or -1
2383 //              if no higher page on block past all previous pairs
2384 //
ndmPastPrevPair(CNDM ndm,ui32 pn)2385 ui32 ndmPastPrevPair(CNDM ndm, ui32 pn) {
2386     ui32 n, po = pn % ndm->pgs_per_blk;
2387 
2388     // First page on block has no previous pairs to skip.
2389     if (po == 0)
2390         return pn;
2391 
2392     // If last page on block is not past previous pairs, no page is.
2393     n = ndm->pgs_per_blk - 1;
2394     if (ndm->pair_offset(n, ndm->dev) < po)
2395         return (ui32)-1;
2396 
2397     // Move backward to find first page whose pair is at a lower offset
2398     // than the input page.
2399     for (;;) {
2400         if (ndm->pair_offset(--n, ndm->dev) < po)
2401             break;
2402         PfAssert(n);
2403     }
2404 
2405     // Return the page that is one page offset higher.
2406     return (pn / ndm->pgs_per_blk) * ndm->pgs_per_blk + n + 1;
2407 }
2408 #endif
2409 #endif // INC_NDM
2410