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