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 // Symbol Definitions
11 #define NDM_META_BLKS 2  // blocks reserved for internal use
12 
13 // Global Variable Declarations
14 CircLink NdmDevs;
15 SEM NdmSem;
16 static int NdmSemCount;
17 #if NV_NDM_CTRL_STORE
18 static ui8 NdmDevCnt;
19 #endif
20 
21 // Local Function Definitions
22 
23 // get_page_status: Examine page to see if it is an NDM control page
24 //
25 //      Inputs: ndm = pointer to NDM control block
26 //              pn = page to examine
27 //
28 //     Returns: -1 if I/O error, NDM_CTRL_BLOCK (2) if control page,
29 //              or NDM_REG_BLOCK (3) if ECC, CRC, sig, or flag fails
30 //
get_page_status(CNDM ndm,ui32 pn)31 static int get_page_status(CNDM ndm, ui32 pn) {
32     int i, j, status;
33     ui32 crc;
34 
35     // Read spare area to check page type. Use read_decode_spare() if
36     // decode is 'free', to avoid second read. Return -1 if I/O error,
37     // regular block if ECC error.
38     if (FLAG_IS_CLR(ndm->flags, FSF_FREE_SPARE_ECC)) {
39         status = ndm->read_spare(pn, ndm->spare_buf, ndm->dev);
40     } else {
41         status = ndm->read_decode_spare(pn, ndm->spare_buf, ndm->dev);
42         if (status == -1)
43             return NDM_REG_BLOCK;
44     }
45     if (status < 0)
46         return FsError(EIO);
47 
48     // Block is regular block if regular page mark is not cleared.
49     if (ONES_UI8(ndm->spare_buf[EB_REG_MARK]) >= 7)
50         return NDM_REG_BLOCK;
51 
52     // If not done already, read decode spare area. Return -1 if fatal
53     // error, regular block if ECC error.
54     if (FLAG_IS_CLR(ndm->flags, FSF_FREE_SPARE_ECC)) {
55         status = ndm->read_decode_spare(pn, ndm->spare_buf, ndm->dev);
56         if (status == -2)
57             return FsError(EIO);
58         else if (status == -1)
59             return NDM_REG_BLOCK;
60     }
61 
62     // Check signature in spare area to ensure this is a control page.
63     for (i = j = 0; i < CTRL_SIG_SZ; ++i) {
64         // Skip the bad block mark in extra bytes.
65         if (i == EB_BBLOCK_MARK)
66             ++j;
67 
68         // Block is regular block if signature is invalid.
69         if (ndm->spare_buf[i + j] != CTRL_SIG[i])
70             return NDM_REG_BLOCK;
71     }
72 
73     // Read main data. Return -1 if fatal err, regular block if ECC err.
74     status = ndm->read_page(pn, ndm->main_buf, ndm->spare_buf, ndm->dev);
75     if (status == -2)
76         return FsError(EIO);
77     else if (status == -1)
78         return NDM_REG_BLOCK;
79 
80     // Perform CRC on page. The 4 CRC bytes are in the page header.
81     // First perform CRC on all but the 4 CRC bytes.
82     for (crc = CRC32_START, i = 0; i < ndm->page_size; ++i) {
83         if (i == HDR_CRC_LOC)
84             i = CTRL_DATA_START;
85         crc = CRC32_UPDATE(crc, ndm->main_buf[i]);
86     }
87 
88     // Now run the CRC bytes through the CRC encoding.
89     for (i = HDR_CRC_LOC; i < CTRL_DATA_START; ++i) //lint !e850
90         crc = CRC32_UPDATE(crc, ndm->main_buf[i]);
91 
92     // If the CRC does not match, page is not control page.
93     if (crc != CRC32_FINAL)
94         return NDM_REG_BLOCK;
95 
96     // Valid signature found. This is a control block.
97     return NDM_CTRL_BLOCK;
98 }
99 
100 // format_status: Check if NDM device is formatted
101 //
102 //      Inputs: ndm = pointer to NDM control block
103 //
104 //     Returns: TRUE if formatted, FALSE unformatted, -1 if error
105 //
format_status(NDM ndm)106 static int format_status(NDM ndm) {
107     ui32 b;
108     int status;
109 
110     // Search reserved good blocks for control info, starting from end.
111     for (b = ndm->num_dev_blks - 1;; --b) {
112         ui32 pn = b * ndm->pgs_per_blk;
113 
114         // Get block's initial good/bad status. Return -1 if error.
115         status = ndm->is_block_bad(pn, ndm->dev);
116         if (status < 0)
117             return FsError(EIO);
118 
119         // If good, check block's first page for control information.
120         if (status == FALSE) {
121             // Get page status. Return -1 if error.
122             status = get_page_status(ndm, pn);
123             if (status == -1)
124                 return -1;
125 
126             // If control info found, device is formatted. Return TRUE.
127             if (status == NDM_CTRL_BLOCK) {
128 #if NDM_DEBUG
129                 printf("NDM formatted - block #%u has control info!\n", b);
130 #endif
131                 PfAssert(ndm->ctrl_blk0 == (ui32)-1);
132                 ndm->ctrl_blk0 = b;
133                 return TRUE;
134             }
135         }
136 
137         // Return FALSE if no metadata in range used for control blocks.
138         if (b == ndm->num_dev_blks - NDM_META_BLKS - ndm->max_bad_blks)
139             return FALSE;
140     }
141 }
142 
143 // get_free_ctrl_blk: Get next free block reserved for replacing bad
144 //              control blocks (starts at highest and goes down)
145 //
146 //       Input: ndm = pointer to NDM control block
147 //
148 //     Returns: Block number of free block or (ui32)-1 if none left
149 //
get_free_ctrl_blk(NDM ndm)150 static ui32 get_free_ctrl_blk(NDM ndm) {
151     ui32 free_b = ndm->free_ctrl_blk;
152 
153     // Move free control block pointer one down, if any free blocks left.
154     if (free_b != (ui32)-1) {
155         ui32 b = free_b - 1;
156 
157         // Skip past initial bad blocks.
158         while (b >= ndm->free_virt_blk && ndmInitBadBlock(ndm, b)) --b;
159 
160         // If above the blocks reserved for swapping bad virtual blocks,
161         // update the free control block pointer. Else fail.
162         if (b >= ndm->free_virt_blk)
163             ndm->free_ctrl_blk = b;
164         else
165             ndm->free_virt_blk = ndm->free_ctrl_blk = (ui32)-1;
166     }
167 
168     // Return free control block number or -1.
169     return free_b;
170 }
171 
172 // set_frst_ndm_block: Compute the cutoff point between virtual area
173 //              and the NDM reserved area
174 //
175 //       Input: ndm = pointer to NDM control block
176 //
set_frst_ndm_block(NDM ndm)177 static void set_frst_ndm_block(NDM ndm) {
178     int i;
179 
180     // There must be enough good (non-initial bad) blocks before the cut
181     // off point to hold all the virtual blocks. Find the lowest offset
182     // past the virtual block count that satisfies this.
183     for (i = 0;; ++i) {
184         // If the offset reaches the number of initially bad blocks, then
185         // there are definitely num_vblks good blocks below this cutoff.
186         if (i == ndm->num_bad_blks)
187             break;
188 
189         // 'i' is the number of initial bad blocks preceding the indexed
190         // initial bad block. Break when the number of volume blocks and
191         // skipped bad blocks is less than the indexed initial bad block.
192         if (ndm->num_vblks + i < ndm->init_bad_blk[i])
193             break;
194     }
195 
196     // The cutoff point is num_vblks plus the determined offset.
197     ndm->frst_reserved = ndm->num_vblks + i;
198 }
199 
200 // init_ibad_list: Initialize list of initially bad blocks
201 //
202 //       Input: ndm = pointer to NDM control block
203 //
204 //     Returns: 0 on success, -1 on error
205 //
init_ibad_list(NDM ndm)206 static int init_ibad_list(NDM ndm) {
207     int status;
208     ui32 b;
209 
210     // Build the initial bad block map by scanning all blocks in order
211     // from lowest to highest. Supports "skip bad block" programming.
212     ndm->num_bad_blks = 0;
213     for (b = 0; b < ndm->num_dev_blks; ++b) {
214         // Get block's initial good/bad status. Return -1 if error.
215         status = ndm->is_block_bad(b * ndm->pgs_per_blk, ndm->dev);
216         if (status < 0)
217             return FsError(EIO);
218 
219         // Check if block is an initial bad block.
220         if (status == TRUE) {
221             // If too many bad blocks encountered, error. Return -1.
222             if (ndm->num_bad_blks >= ndm->max_bad_blks)
223                 return FsError(EINVAL);
224 
225             // Add block to initial bad blocks array and increment bad count.
226             ndm->init_bad_blk[ndm->num_bad_blks] = b;
227 #if NDM_DEBUG
228             printf("init_ibad_lis: adding block #%u to init_bad_blk[%u]\n", b, ndm->num_bad_blks);
229 #endif
230             ++ndm->num_bad_blks;
231         }
232     }
233 
234     // Set the last initial bad block entry to the device block count.
235     ndm->init_bad_blk[ndm->num_bad_blks] = ndm->num_dev_blks;
236 #if NDM_DEBUG
237     printf("init_ibad_lis: LAST init_bad_blk[%u] = %u\n", ndm->num_bad_blks, ndm->num_dev_blks);
238 #endif
239 
240     // Return success.
241     return 0;
242 }
243 
244 //  ndm_format: Format previously unformatted device
245 //
246 //       Input: ndm = pointer to NDM control block
247 //
248 //     Returns: 0 on success, -1 on error
249 //
ndm_format(NDM ndm)250 static int ndm_format(NDM ndm) {
251     ui32 b;
252 
253 #if NV_NDM_CTRL_STORE
254     // Invalidate the saved first page of NDM control information.
255     NvNdmCtrlPgWr(0);
256 #endif
257 
258     // Build the initial bad block map by scanning all blocks in order.
259     if (init_ibad_list(ndm))
260         return -1;
261 
262     // Compute the cutoff between virtual blocks and reserved blocks.
263     set_frst_ndm_block(ndm);
264 
265     // Set the free control block (last good block) and free volume
266     // block (first good block after cutoff) pointers.
267     for (b = ndm->frst_reserved; b < ndm->num_dev_blks; ++b) {
268         // If initial bad block, skip it.
269         if (ndmInitBadBlock(ndm, b))
270             continue;
271 
272         // If first free block pointer is not set, set it.
273         if (ndm->free_virt_blk == (ui32)-1)
274             ndm->free_virt_blk = b;
275 
276         // Set the last good block as the start of the metadata blocks.
277         ndm->free_ctrl_blk = b;
278     }
279 
280     // The last two good free blocks are used for control information.
281     ndm->ctrl_blk0 = get_free_ctrl_blk(ndm);
282     ndm->ctrl_blk1 = get_free_ctrl_blk(ndm);
283     if (ndm->ctrl_blk1 == (ui32)-1)
284         return FsError(ENOSPC);
285 #if NDM_DEBUG
286     printf("NDM ctrl_blk0=%u, ctrl_blk1=%u\n", ndm->ctrl_blk0, ndm->ctrl_blk1);
287 #endif
288 
289     // Begin the first control write on ctrl_blk0.
290     ndm->next_ctrl_start = ndm->ctrl_blk0 * ndm->pgs_per_blk;
291 
292     // Write initial control information and return status.
293     ndm->xfr_tblk = (ui32)-1;
294     return ndmWrCtrl(ndm);
295 }
296 
297 // find_last_ctrl_info: Find last valid control information
298 //
299 //       Input: ndm = pointer to NDM control block
300 //
301 //     Returns: 0 if found, -1 otherwise
302 //
find_last_ctrl_info(NDM ndm)303 static int find_last_ctrl_info(NDM ndm) {
304     ui32 b, p, high_seq = (ui32)-1, curr_seq;
305     ui32 p_beg, p_end, last_ctrl_p = 0, ctrl_pages = 0;
306     ui16 curr_p, last_p;
307     int page_status;
308 
309 #if NV_NDM_CTRL_STORE
310     // Check if number of first control information page has been saved.
311     p = NvNdmCtrlPgRd();
312     if (p) {
313         // Get the page status. If I/O error, return -1.
314         page_status = get_page_status(ndm, p);
315         if (page_status == -1)
316             return -1;
317 
318         // Check if it is a control page.
319         if (page_status == NDM_CTRL_BLOCK) {
320             // Check if it is the last page of a control information write.
321             curr_p = RD16_LE(&ndm->main_buf[HDR_CURR_LOC]);
322             ctrl_pages = RD16_LE(&ndm->main_buf[HDR_LAST_LOC]);
323             if (curr_p == ctrl_pages) {
324                 // Read its (the highest) sequence number and use this page.
325                 high_seq = RD32_LE(&ndm->main_buf[HDR_SEQ_LOC]);
326                 last_ctrl_p = p;
327             }
328         }
329     }
330 
331     // If last control page not known from NVRAM, search all reserved
332     // blocks for it: from ctrl_blk0 (highest block w/control info) to
333     // num_vblks.
334     if (last_ctrl_p == 0)
335 #endif
336     {
337         for (b = ndm->ctrl_blk0; b >= ndm->num_vblks; --b) {
338             // Get first and last page numbers to search.
339             p_beg = b * ndm->pgs_per_blk;
340             p_end = p_beg + ndm->pgs_per_blk - 1;
341 
342             // Check if not block that format_status() found metadata on.
343             if (b != ndm->ctrl_blk0) {
344                 // Get status of block's first page. If error, return -1.
345                 page_status = get_page_status(ndm, p_beg);
346                 if (page_status == -1)
347                     return -1;
348 
349                 // Skip block if its first page is not a control page.
350                 if (page_status != NDM_CTRL_BLOCK)
351                     continue;
352             }
353 
354             // Search block from end to beginning for last control page.
355             for (p = p_end; p >= p_beg; --p) {
356                 // Get the page status. If error, return -1.
357                 page_status = get_page_status(ndm, p);
358                 if (page_status == -1)
359                     return -1;
360 
361                 // If page is not control page, skip it.
362                 if (page_status != NDM_CTRL_BLOCK)
363                     continue;
364 
365                 // If this is not the last page of a control info, skip it.
366                 curr_p = RD16_LE(&ndm->main_buf[HDR_CURR_LOC]);
367                 last_p = RD16_LE(&ndm->main_buf[HDR_LAST_LOC]);
368                 if (curr_p != last_p)
369                     continue;
370 
371                 // Read current sequence number.
372                 curr_seq = RD32_LE(&ndm->main_buf[HDR_SEQ_LOC]);
373 
374                 // If first 'last page' found or most recent, remember it.
375                 if (high_seq == (ui32)-1 || curr_seq > high_seq) {
376                     // Remember its sequence number and first/last page numbers.
377                     high_seq = curr_seq;
378                     last_ctrl_p = p;
379                     ctrl_pages = last_p;
380 #if NDM_DEBUG
381                     printf(
382                         "find_ctrl: seq #%u, last = %u (block = %u), "
383                         "# pages = %u\n",
384                         high_seq, last_ctrl_p, last_ctrl_p / ndm->pgs_per_blk, ctrl_pages);
385 #endif
386                 }
387 
388                 // Break to search next block.
389                 break;
390             }
391         }
392     }
393 
394     // If no last control page found, no control information on device.
395     if (high_seq == (ui32)-1)
396         return -1;
397 
398     // Save information found so far.
399     ndm->last_ctrl_page = last_ctrl_p;
400     ndm->ctrl_pages = ctrl_pages;
401     ndm->ctrl_seq = high_seq;
402 
403     // If control information is only one page, finish (success!) here.
404     if (ctrl_pages == 1) {
405         ndm->frst_ctrl_page = last_ctrl_p;
406         return 0;
407     }
408 
409     // Search for first page of latest control info in block with last.
410     p_end = (last_ctrl_p / ndm->pgs_per_blk) * ndm->pgs_per_blk;
411     for (p = last_ctrl_p - 1; p >= p_end; --p) {
412         // Get the page status. If error, return -1.
413         page_status = get_page_status(ndm, p);
414         if (page_status == -1)
415             return -1;
416 
417         // If page is not control page, skip it.
418         if (page_status != NDM_CTRL_BLOCK)
419             continue;
420 
421         // If matching first control page found, return success.
422         curr_seq = RD32_LE(&ndm->main_buf[HDR_SEQ_LOC]);
423         curr_p = RD16_LE(&ndm->main_buf[HDR_CURR_LOC]);
424         last_p = RD16_LE(&ndm->main_buf[HDR_LAST_LOC]);
425         if (curr_p == 1 && curr_seq == high_seq && last_p == ctrl_pages) {
426 #if NDM_DEBUG
427             printf("find_ctrl: first = %u (block = %u)\n", p, p / ndm->pgs_per_blk);
428 #endif
429             ndm->frst_ctrl_page = p;
430             return 0;
431         }
432     }
433 
434     // First page wasn't found, scan all other NDM reserved blocks.
435     for (b = ndm->ctrl_blk0; b >= ndm->num_vblks; --b) {
436         // Skip the scanned block.
437         if (b == last_ctrl_p / ndm->pgs_per_blk)
438             continue;
439 
440         // Get first and last page numbers to search.
441         p_beg = b * ndm->pgs_per_blk;
442         p_end = p_beg + ndm->pgs_per_blk - 1;
443 
444         // Start scanning the pages in the block.
445         for (p = p_beg; p <= p_end; ++p) {
446             // Get the page status. If error, return -1.
447             page_status = get_page_status(ndm, p);
448             if (page_status == -1)
449                 return -1;
450 
451             // If page is not control page, skip it.
452             if (page_status != NDM_CTRL_BLOCK)
453                 continue;
454 
455             // If first control page found, return success.
456             curr_seq = RD32_LE(&ndm->main_buf[HDR_SEQ_LOC]);
457             curr_p = RD16_LE(&ndm->main_buf[HDR_CURR_LOC]);
458             last_p = RD16_LE(&ndm->main_buf[HDR_LAST_LOC]);
459             if (curr_p == 1 && curr_seq == high_seq && last_p == ctrl_pages) {
460 #if NDM_DEBUG
461                 printf("find_ctrl: first = %u (block = %u)\n", p, p / ndm->pgs_per_blk);
462 #endif
463                 ndm->frst_ctrl_page = p;
464                 return 0;
465             }
466         }
467     }
468 
469     // First control page not found, return -1.
470     return -1;
471 }
472 
473 // is_next_ctrl_page: Determine if page is next in control sequence
474 //
475 //      Inputs: ndm = pointer to NDM control block
476 //              pn = page to analyze
477 //              curr_num = current number in control sequence
478 //
479 //     Returns: NDM_CTRL_BLOCK iff page next one, NDM_REG_BLOCK if
480 //              not, -1 on error
481 //
is_next_ctrl_page(CNDM ndm,ui32 pn,ui16 curr_num)482 static int is_next_ctrl_page(CNDM ndm, ui32 pn, ui16 curr_num) {
483     int page_status;
484 
485     // Get the page status.
486     page_status = get_page_status(ndm, pn);
487     if (page_status != NDM_CTRL_BLOCK)
488         return page_status;
489 
490     // Read page in. Return -1 if error (ECC or fatal).
491     if (ndm->read_page(pn, ndm->main_buf, ndm->spare_buf, ndm->dev) < 0)
492         return FsError(EIO);
493 
494     // Determine if this is the next control page in sequence.
495     if (RD16_LE(&ndm->main_buf[HDR_CURR_LOC]) == curr_num + 1 &&
496         RD16_LE(&ndm->main_buf[HDR_LAST_LOC]) == ndm->ctrl_pages &&
497         RD32_LE(&ndm->main_buf[HDR_SEQ_LOC]) == ndm->ctrl_seq)
498         return NDM_CTRL_BLOCK;
499 
500     // Else this is a regular block.
501     return NDM_REG_BLOCK;
502 }
503 
504 // get_next_ctrl_page: Retrieve next page in control info
505 //
506 //      Inputs: ndm = pointer to NDM control block
507 //              curr_p = current control page
508 //
509 //     Returns: Next control page, -1 on error
510 //
get_next_ctrl_page(CNDM ndm,ui32 curr_p)511 static ui32 get_next_ctrl_page(CNDM ndm, ui32 curr_p) {
512     ui16 curr_num;
513     ui32 p, p_end;
514     int page_status;
515 
516     // Retrieve current number in control info sequence.
517     curr_num = RD16_LE(&ndm->main_buf[HDR_CURR_LOC]);
518 
519     // If there's no next control page according to header, return -1.
520     if (curr_num >= ndm->ctrl_pages)
521         return (ui32)FsError(EINVAL);
522 
523     // Look for page in same block first.
524     for (p = curr_p + 1; p % ndm->pgs_per_blk; ++p) {
525         page_status = is_next_ctrl_page(ndm, p, curr_num);
526         if (page_status == NDM_CTRL_BLOCK)
527             return p;
528         else if (page_status == -1)
529             return (ui32)-1;
530     }
531 
532     // Get first and last page numbers in opposing control block.
533     if (curr_p / ndm->pgs_per_blk == ndm->ctrl_blk0)
534         p = ndm->ctrl_blk1 * ndm->pgs_per_blk;
535     else
536         p = ndm->ctrl_blk0 * ndm->pgs_per_blk;
537     p_end = p + ndm->pgs_per_blk - 1;
538 
539     // Search second control block for next control page.
540     do {
541         page_status = is_next_ctrl_page(ndm, p, curr_num);
542         if (page_status == NDM_CTRL_BLOCK)
543             return p;
544         else if (page_status == -1)
545             return (ui32)-1;
546     } while (++p <= p_end);
547 
548     // At this point, no next page can be found.
549     return (ui32)FsError(EINVAL);
550 }
551 
552 // check_next_read: If next read spans control pages, adjust the
553 //                  current control information pointers
554 //
555 //       Input: ndm = pointer to NDM control block
556 //   In/Output:curr_loc = current location in control page
557 //   In/Output:pn = current control page
558 //   In/Output:ctrl_pages = control pages read so far
559 //       Input:  size = size of next read in bytes
560 //
561 //     Returns: 0 on success, -1 on failure
562 //
check_next_read(CNDM ndm,ui32 * curr_loc,ui32 * pn,ui32 * ctrl_pages,ui32 size)563 static int check_next_read(CNDM ndm, ui32* curr_loc, ui32* pn, ui32* ctrl_pages, ui32 size) {
564     // If next read goes past end of current control page, read next.
565     if (*curr_loc + size > ndm->page_size) {
566         // Figure out where the next page is.
567         *pn = get_next_ctrl_page(ndm, *pn);
568         if (*pn == (ui32)-1)
569             return -1;
570 
571         // Reset current control location to beginning of the next page.
572         *curr_loc = CTRL_DATA_START;
573         *ctrl_pages += 1;
574 
575         // Read the next page. Return -1 if error (ECC or fatal).
576         if (ndm->read_page(*pn, ndm->main_buf, ndm->spare_buf, ndm->dev) < 0)
577             return FsError(EIO);
578 #if NDM_DEBUG
579         printf("read_ctrl: READ page #%u\n", *pn);
580 #endif
581     }
582 
583     // Return success.
584     return 0;
585 }
586 
587 // read_ctrl_info: Read the NDM control information
588 //
589 //       Input: ndm = pointer to NDM control block
590 //
591 //     Returns: 0 on success, -1 on failure
592 //
read_ctrl_info(NDM ndm)593 static int read_ctrl_info(NDM ndm) {
594     ui32 bn, vbn, i, curr_loc = CTRL_DATA_START, ctrl_pages = 1;
595     ui32 p = ndm->frst_ctrl_page;
596 
597     // Read the first control page. Return -1 if error (ECC or fatal).
598     if (ndm->read_page(p, ndm->main_buf, ndm->spare_buf, ndm->dev) < 0)
599         return FsError(EIO);
600 #if NDM_DEBUG
601     printf("read_ctrl: READ page #%u\n", p);
602 #endif
603 
604     // Ensure the number of blocks and block size are correct.
605     if (ndm->num_dev_blks != RD32_LE(&ndm->main_buf[curr_loc]))
606         return FsError(EINVAL);
607     curr_loc += sizeof(ui32);
608     if (ndm->block_size != RD32_LE(&ndm->main_buf[curr_loc]))
609         return FsError(EINVAL);
610     curr_loc += sizeof(ui32);
611 
612     // Retrieve the control block pointers.
613     ndm->ctrl_blk0 = RD32_LE(&ndm->main_buf[curr_loc]);
614     curr_loc += sizeof(ui32);
615     ndm->ctrl_blk1 = RD32_LE(&ndm->main_buf[curr_loc]);
616     curr_loc += sizeof(ui32);
617 
618     // Retrieve free_virt_blk pointer.
619     ndm->free_virt_blk = RD32_LE(&ndm->main_buf[curr_loc]);
620     curr_loc += sizeof(ui32);
621 
622     // Retrieve free_ctrl_blk pointer.
623     ndm->free_ctrl_blk = RD32_LE(&ndm->main_buf[curr_loc]);
624     curr_loc += sizeof(ui32);
625 
626 #if NDM_DEBUG
627     printf("read_ctrl info:\n");
628     printf("  -> ctrl_seq    = %u\n", ndm->ctrl_seq);
629     printf("  -> ctrl_blk0   = %u\n", ndm->ctrl_blk0);
630     printf("  -> ctrl_blk1   = %u\n", ndm->ctrl_blk1);
631     printf("  -> free_virt_blk = %u\n", ndm->free_virt_blk);
632     printf("  -> free_ctrl_blk = %u\n", ndm->free_ctrl_blk);
633 #endif
634 
635     // Retrieve the transfer to block (if any).
636     ndm->xfr_tblk = RD32_LE(&ndm->main_buf[curr_loc]);
637     curr_loc += sizeof(ui32);
638 
639     // If a bad block was being transferred, retrieve all other relevant
640     // information about it so that the transfer can be redone.
641     if (ndm->xfr_tblk != (ui32)-1) {
642         // Retrieve the physical bad block being transferred.
643         ndm->xfr_fblk = RD32_LE(&ndm->main_buf[curr_loc]);
644         curr_loc += sizeof(ui32);
645 
646         // Retrieve the page offset of bad page in bad block.
647         ndm->xfr_bad_po = RD32_LE(&ndm->main_buf[curr_loc]);
648         curr_loc += sizeof(ui32);
649 
650         // Skip obsolete full/partial transfer flag.
651         ++curr_loc;
652 
653 #if NDM_DEBUG
654         printf("  -> xfr_tblk       = %u\n", ndm->xfr_tblk);
655         printf("  -> xfr_fblk       = %u\n", ndm->xfr_fblk);
656         printf("  -> xfr_bad_po     = %u\n", ndm->xfr_bad_po);
657 #endif
658     }
659 
660 #if NDM_DEBUG
661     else
662         puts("  -> xfr_tblk       = -1");
663 #endif
664 
665     // Retrieve the number of partitions.
666     ndm->num_partitions = RD32_LE(&ndm->main_buf[curr_loc]);
667     curr_loc += sizeof(ui32);
668 #if NDM_DEBUG
669     printf("  -> num_partitions = %u\n", ndm->num_partitions);
670     printf("read_ctrl: init_bad_blk[]\n");
671 #endif
672 
673     // Retrieve the initial bad blocks map.
674     for (ndm->num_bad_blks = i = 0;; ++i) {
675         // If too many initial bad blocks, error.
676         if (ndm->num_bad_blks > ndm->max_bad_blks)
677             return FsError(EINVAL);
678 
679         // If next read spans control pages, adjust. Return -1 if error.
680         if (check_next_read(ndm, &curr_loc, &p, &ctrl_pages, sizeof(ui32)))
681             return -1;
682 
683         // Retrieve the next initial bad block.
684         bn = RD32_LE(&ndm->main_buf[curr_loc]);
685         curr_loc += sizeof(ui32);
686 
687 #if NDM_DEBUG
688         printf("    [%u] = %u\n", i, bn);
689 #endif
690 
691         // Store block in initial bad block map and check for end of map.
692         ndm->init_bad_blk[i] = bn;
693         if (bn == ndm->num_dev_blks)
694             break;
695 
696         // Adjust running count of bad blocks to account for this one.
697         ++ndm->num_bad_blks;
698     }
699 
700 #if NDM_DEBUG
701     puts("read_ctrl: run_bad_blk[]");
702 #endif
703 
704     // Retrieve running bad blocks map.
705     for (ndm->num_rbb = 0;; ++ndm->num_rbb) {
706         // If too many bad blocks, error.
707         if (ndm->num_bad_blks > ndm->max_bad_blks)
708             return FsError(EINVAL);
709 
710         // If next read spans control pages, adjust. Return -1 if error.
711         if (check_next_read(ndm, &curr_loc, &p, &ctrl_pages, 2 * sizeof(ui32)))
712             return -1;
713 
714         // Retrieve the next running bad block pair.
715         vbn = RD32_LE(&ndm->main_buf[curr_loc]);
716         curr_loc += sizeof(ui32);
717         bn = RD32_LE(&ndm->main_buf[curr_loc]);
718         curr_loc += sizeof(ui32);
719 
720         // If end of running blocks reached, stop.
721         if (vbn == (ui32)-1 && bn == (ui32)-1)
722             break;
723 
724         // Store bad block pair in running block map.
725         ndm->run_bad_blk[ndm->num_rbb].key = vbn;
726         ndm->run_bad_blk[ndm->num_rbb].val = bn;
727 
728 #if NDM_DEBUG
729         printf("    [%u] key = %u, val = %u\n", ndm->num_rbb, vbn, bn);
730 #endif
731 
732         // Adjust running count of bad blocks to account for this one.
733         ++ndm->num_bad_blks;
734     }
735 
736     // Retrieve the NDM partitions if any.
737     if (ndm->num_partitions) {
738         // If not already done, allocate memory for partitions table.
739         if (ndm->partitions == NULL) {
740             ndm->partitions = FsCalloc(ndm->num_partitions, sizeof(NDMPartition));
741             if (ndm->partitions == NULL)
742                 return -1;
743         }
744 
745 #if NDM_DEBUG
746         puts("read_ctrl: partitions[]");
747 #endif
748 
749         // Read partitions from the control information one at a time.
750         for (i = 0; i < ndm->num_partitions; ++i) {
751 #if NDM_PART_USER
752             ui32 j;
753 #endif
754 
755             // If next read spans control pages, adjust. Return -1 if error.
756             if (check_next_read(ndm, &curr_loc, &p, &ctrl_pages, sizeof(NDMPartition)))
757                 return -1;
758 
759             // Retrieve the next partition. Read partition first block.
760             ndm->partitions[i].first_block = RD32_LE(&ndm->main_buf[curr_loc]);
761             curr_loc += sizeof(ui32);
762 
763             // Read partition number of blocks.
764             ndm->partitions[i].num_blocks = RD32_LE(&ndm->main_buf[curr_loc]);
765             curr_loc += sizeof(ui32);
766 
767 #if NDM_PART_USER
768             // Read the user defined ui32s.
769             for (j = 0; j < NDM_PART_USER; ++j) {
770                 ndm->partitions[i].user[j] = RD32_LE(&ndm->main_buf[curr_loc]);
771                 curr_loc += sizeof(ui32);
772             }
773 #endif
774 
775             // Read the partition name.
776             strncpy(ndm->partitions[i].name, (char*)&ndm->main_buf[curr_loc],
777                     NDM_PART_NAME_LEN - 1);
778             curr_loc += NDM_PART_NAME_LEN;
779 
780             // Read the partition type.
781             ndm->partitions[i].type = ndm->main_buf[curr_loc++];
782 
783 #if NDM_DEBUG
784             printf(" partition[%2u]:\n", i);
785             printf("   - name        = %s\n", ndm->partitions[i].name);
786             printf("   - first block = %u\n", ndm->partitions[i].first_block);
787             printf("   - num blocks  = %u\n", ndm->partitions[i].num_blocks);
788 #if NDM_PART_USER
789             for (j = 0; j < NDM_PART_USER; ++j)
790                 printf("   - user[%u]     = %u\n", j, ndm->partitions[i].user[j]);
791 #endif
792             printf("   - type        = ");
793             switch (ndm->partitions[i].type) {
794                 case 0:
795                     puts("NONE");
796                     break;
797                 case FFS_VOL:
798                     puts("FFS");
799                     break;
800                 case FAT_VOL:
801                     puts("FAT");
802                     break;
803                 default:
804                     printf("%u\n", ndm->partitions[i].type);
805                     break;
806             }
807 #endif // NDM_DEBUG
808         }
809     }
810 
811     // Check that number of read pages agrees with recorded one.
812     if (ctrl_pages != ndm->ctrl_pages || p != ndm->last_ctrl_page)
813         return FsError(EINVAL);
814 
815     // Return success.
816     return 0;
817 }
818 
819 // recover_bad_blk: Recover from an unexpected power down while a bad
820 //              block was being transferred
821 //
822 //       Input: ndm = pointer to NDM control block
823 //
824 //     Returns: 0 on success, -1 on failure
825 //
recover_bad_blk(NDM ndm)826 static int recover_bad_blk(NDM ndm) {
827     ui32 i, bpn;
828     int rc;
829 
830     // Ensure the 'transfer from' block value is valid.
831     if (ndm->xfr_fblk >= ndm->num_dev_blks)
832         return FsError(EINVAL);
833 
834     // Ensure the 'transfer to' block value is valid.
835     if (ndm->xfr_tblk < ndm->frst_reserved || ndm->xfr_tblk >= ndm->free_virt_blk)
836         return FsError(EINVAL);
837 
838     // Erase the 'transfer to' block. Return if fatal error.
839     rc = ndm->erase_block(ndm->xfr_tblk * ndm->pgs_per_blk, ndm->dev);
840     if (rc == -2)
841         return FsError(EIO);
842 
843     // Check if block erase command failed.
844     if (rc < 0) {
845         // Adjust bad block count. If too many, error.
846         if (++ndm->num_bad_blks > ndm->max_bad_blks)
847             return FsError(ENOSPC);
848 
849         // Find running list entry with this 'transfer from/to' block pair.
850         for (i = 0;; ++i) {
851             if (i == ndm->num_rbb)
852                 return FsError(EINVAL);
853             if (ndm->run_bad_blk[i].key == ndm->xfr_fblk &&
854                 ndm->run_bad_blk[i].val == ndm->xfr_tblk)
855                 break;
856         }
857 
858         // Invalidate the 'transfer to' block since it's bad now.
859         ndm->run_bad_blk[i].val = (ui32)-1;
860 
861         // Add new bad block list entry with this 'transfer to' block.
862         ndm->run_bad_blk[ndm->num_rbb].key = ndm->xfr_tblk;
863         ndm->run_bad_blk[ndm->num_rbb].val = (ui32)-1;
864         ++ndm->num_rbb;
865     }
866 
867     // Else erase was successful. Adjust free block pointer.
868     else {
869         PfAssert(ndm->free_virt_blk == (ui32)-1 || ndm->xfr_tblk + 1 == ndm->free_virt_blk);
870         ndm->free_virt_blk = ndm->xfr_tblk;
871     }
872 
873     // Reset 'transfer to' block pointer.
874     ndm->xfr_tblk = (ui32)-1;
875 
876     // Figure out bad page number on bad block.
877     bpn = ndm->xfr_fblk * ndm->pgs_per_blk + ndm->xfr_bad_po;
878 
879     // Mark block bad and do bad block recovery for write failure.
880     return ndmMarkBadBlock(ndm, bpn, WRITE_PAGE);
881 }
882 
883 //    init_ndm: Initialize an NDM by reading the flash
884 //
885 //       Input: ndm = pointer to NDM control block
886 //
887 //     Returns: 0 on success, -1 on failure
888 //
init_ndm(NDM ndm)889 static int init_ndm(NDM ndm) {
890     int is_formatted, wr_metadata;
891     ui32 ctrl_blk;
892 
893     // Analyze device to see if it is formatted. Return -1 if error.
894     is_formatted = format_status(ndm);
895     if (is_formatted < 0)
896         return -1;
897 
898     // If device not NDM formatted, scan and format it. Return status.
899     if (!is_formatted)
900         return ndm_format(ndm);
901 
902     // Else device is NDM formatted. Find latest control information.
903     if (find_last_ctrl_info(ndm))
904         return -1;
905 
906     // Read the control information. Return -1 if error.
907     PfAssert(ndm->ctrl_blk1 == (ui32)-1);
908     if (read_ctrl_info(ndm))
909         return -1;
910 
911     // Set flag if configured to write metadata at every startup to
912     // ensure control block reads don't create read-disturb errors.
913     wr_metadata = FLAG_IS_SET(ndm->flags, FSF_NDM_INIT_WRITE);
914 
915     // Match the block the control information was found on with either
916     // ctrl_blk0 or ctrl_blk1, pick other block for next control write.
917     ctrl_blk = ndm->last_ctrl_page / ndm->pgs_per_blk;
918     if (ctrl_blk == ndm->ctrl_blk0)
919         ctrl_blk = ndm->ctrl_blk1;
920     else if (ctrl_blk == ndm->ctrl_blk1)
921         ctrl_blk = ndm->ctrl_blk0;
922 
923     // Else this must be the first start from a preprogrammed image.
924     else {
925         // Fail start-up if image's running bad block count is non-zero.
926         if (ndm->num_rbb)
927             return FsError(ENXIO);
928 
929         // Redo the initial bad block list for our device.
930         if (init_ibad_list(ndm))
931             return -1;
932 
933         // Request first metadata write and have it on ctrl_blk0.
934         ctrl_blk = ndm->ctrl_blk0;
935         wr_metadata = TRUE;
936     }
937 
938     // Assign starting control write page number.
939     ndm->next_ctrl_start = ctrl_blk * ndm->pgs_per_blk;
940 
941     // Compute the cutoff between virtual blocks and reserved blocks.
942     set_frst_ndm_block(ndm);
943 
944     // Ensure even lowest running bad block lies in reserved area.
945     if (ndm->run_bad_blk[0].val < ndm->frst_reserved)
946         return -1;
947 
948     // If in the middle of transferring a bad block, continue transfer.
949     if (ndm->xfr_tblk != (ui32)-1)
950         return recover_bad_blk(ndm);
951 
952     // Write NDM control information if needed.
953     if (wr_metadata) {
954         ndm->xfr_tblk = (ui32)-1;
955         return ndmWrCtrl(ndm);
956     } else
957         return 0;
958 }
959 
960 // ndm_xfr_page: Substitute if driver does not supply transfer_page()
961 //
962 //     Inputs: old_pn = old page number
963 //             new_pn = new page number
964 //             buf = pointer to temperary buffer for page data
965 //             old_spare = buffer to hold old page spare contents
966 //             new_spare = buffer to hold new page spare contents
967 //             encode_spare = flag to encode/not encode spare bytes
968 //                            1 through 14 (FTL encodes/FFS does not)
969 //             ndm_ptr = pointer to TargetNDM control block
970 //
971 //    Returns: 0 on success, -2 on fatal error, -1 on chip error,
972 //             1 on ECC decode error
973 //
ndm_xfr_page(ui32 old_pn,ui32 new_pn,ui8 * buf,ui8 * old_spare,ui8 * new_spare,int encode_spare,void * ndm_ptr)974 static int ndm_xfr_page(ui32 old_pn, ui32 new_pn, ui8* buf, ui8* old_spare, ui8* new_spare,
975                         int encode_spare, void* ndm_ptr) {
976     int status;
977     NDM ndm = ndm_ptr;
978 
979     // Read page data. Return is 1, 0, -2, or -1.
980     status = ndm->read_page(old_pn, buf, old_spare, ndm->dev);
981 
982     // Error check: return 1 for ECC decode error, else -2 fatal error.
983     if (status < 0) {
984         if (status == -1)
985             return 1;
986         FsError(EIO);
987         return -2;
988     }
989 
990     // Write data page to flash and return status.
991     return ndm->write_page(new_pn, buf, new_spare, encode_spare, ndm->dev);
992 }
993 
994 // Global Function Definitions
995 
996 //   NdmModule: NDM interface to software object manager
997 //
998 //       Input: req = module request code
999 //              ... = additional parameters specific to request
1000 //
NdmModule(int req,...)1001 void* NdmModule(int req, ...) {
1002     switch (req) {
1003         case kInitMod: {
1004             // Initialize the devices list.
1005             CIRC_LIST_INIT(&NdmDevs);
1006 
1007             // Create the NDM global synchronization semaphore.
1008             NdmSem = semCreate("NDM_SEM", 1, OS_FIFO);
1009             if (NdmSem == NULL)
1010                 return (void*)-1;
1011         }
1012     }
1013 
1014     return NULL;
1015 }
1016 
1017 //   ndmAddDev: Create a new NDM
1018 //
1019 //       Input: dvr = pointer to NDM driver control block
1020 //
1021 //     Returns: New NDM control block on success, NULL on error
1022 //
ndmAddDev(const NDMDrvr * dvr)1023 NDM ndmAddDev(const NDMDrvr* dvr) {
1024     char sem_name[9];
1025     uint eb_alloc_sz;
1026     NDM ndm;
1027 
1028 #if NV_NDM_CTRL_STORE
1029     // Can only use one NDM device with NVRAM control page storage.
1030     if (NdmDevCnt > 0) {
1031         FsError(EINVAL);
1032         return NULL;
1033     }
1034 #endif
1035 
1036     // Error if unsupported flash type.
1037     if (dvr->type != NDM_MLC && dvr->type != NDM_SLC) {
1038         FsError(EINVAL);
1039         return NULL;
1040     }
1041 
1042 #if !INC_FFS_NDM_MLC && !INC_FTL_NDM_MLC
1043     // Error if type is NDM_MLC and no MLC support is enabled.
1044     if (dvr->type == NDM_MLC) {
1045         FsError(EINVAL);
1046         return NULL;
1047     }
1048 #endif
1049 
1050 #if !INC_FFS_NDM_SLC && !INC_FTL_NDM_SLC
1051     // Error if type is NDM_SLC and no SLC support is enabled.
1052     if (dvr->type == NDM_SLC) {
1053         FsError(EINVAL);
1054         return NULL;
1055     }
1056 #endif
1057 
1058 #if OS_PARM_CHECK
1059     // Ensure NDM driver flags are valid.
1060     if (dvr->flags &
1061         ~(FSF_MULTI_ACCESS | FSF_TRANSFER_PAGE | FSF_FREE_SPARE_ECC | FSF_NDM_INIT_WRITE)) {
1062         FsError(EINVAL);
1063         return NULL;
1064     }
1065 #endif
1066 
1067     // Check for valid number of blocks.
1068     if (dvr->num_blocks <= dvr->max_bad_blocks + NDM_META_BLKS) {
1069         FsError(EINVAL);
1070         return NULL;
1071     }
1072 
1073     // Check for valid page size (multiple of 512).
1074     if (dvr->page_size == 0 || dvr->page_size % 512) {
1075         FsError(EINVAL);
1076         return NULL;
1077     }
1078 
1079     // Check for valid spare bytes size.
1080     if (dvr->eb_size > dvr->page_size || dvr->eb_size < 16) {
1081         FsError(EINVAL);
1082         return NULL;
1083     }
1084 
1085     // Allocate space for TargetNDM control block.
1086     ndm = FsCalloc(1, sizeof(struct ndm));
1087     if (ndm == NULL)
1088         return NULL;
1089 
1090     // Acquire exclusive access to global NDM semaphore.
1091     semPend(NdmSem, WAIT_FOREVER);
1092 
1093     // Add device to list of NDM devices.
1094     CIRC_LIST_APPEND(&ndm->link, &NdmDevs);
1095 
1096     // Set the number of virtual blocks.
1097     ndm->num_vblks = dvr->num_blocks - dvr->max_bad_blocks - NDM_META_BLKS;
1098 
1099     // Set the other driver dependent variables.
1100     ndm->num_dev_blks = dvr->num_blocks;
1101     ndm->max_bad_blks = dvr->max_bad_blocks;
1102     ndm->block_size = dvr->block_size;
1103     ndm->page_size = dvr->page_size;
1104     ndm->eb_size = dvr->eb_size;
1105     ndm->flags = dvr->flags;
1106 
1107     // Allocate memory for initial and running bad blocks arrays.
1108     ndm->init_bad_blk = FsMalloc((ndm->max_bad_blks + 1) * sizeof(ui32));
1109     if (ndm->init_bad_blk == NULL)
1110         goto ndmAddDev_err;
1111     ndm->run_bad_blk = FsMalloc((ndm->max_bad_blks + 1) * sizeof(Pair));
1112     if (ndm->run_bad_blk == NULL)
1113         goto ndmAddDev_err;
1114 
1115     // Initialize the initial and running bad block arrays.
1116     memset(ndm->init_bad_blk, 0xFF, (ndm->max_bad_blks + 1) * sizeof(ui32));
1117     memset(ndm->run_bad_blk, 0xFF, (ndm->max_bad_blks + 1) * sizeof(Pair));
1118 
1119     // Create the access semaphore.
1120     sprintf(sem_name, "NDM_S%03d", NdmSemCount++);
1121     ndm->sem = semCreate(sem_name, 1, OS_FIFO);
1122     if (ndm->sem == NULL)
1123         goto ndmAddDev_err;
1124 
1125     // Ensure spare area buffers allocated by NDM are cache aligned.
1126     eb_alloc_sz = ndm->eb_size;
1127 #if CACHE_LINE_SIZE
1128     eb_alloc_sz = ((eb_alloc_sz + CACHE_LINE_SIZE - 1) / CACHE_LINE_SIZE) * CACHE_LINE_SIZE;
1129 #endif
1130 
1131     // Allocate memory for page main and spare data buffers.
1132     ndm->main_buf = FsAalloc(ndm->page_size + 2 * eb_alloc_sz);
1133     if (ndm->main_buf == NULL)
1134         goto ndmAddDev_err;
1135     ndm->spare_buf = ndm->main_buf + ndm->page_size;
1136     ndm->tmp_spare = ndm->spare_buf + eb_alloc_sz;
1137 
1138     // Initialize all other NDM variables.
1139     ndm->ctrl_blk0 = ndm->ctrl_blk1 = (ui32)-1;
1140     ndm->frst_ctrl_page = ndm->last_ctrl_page = (ui32)-1;
1141     ndm->free_virt_blk = ndm->free_ctrl_blk = (ui32)-1;
1142     ndm->ctrl_seq = (ui32)-1;
1143     ndm->pgs_per_blk = ndm->block_size / ndm->page_size;
1144     ndm->xfr_tblk = ndm->xfr_fblk = ndm->xfr_bad_po = (ui32)-1;
1145     ndm->last_wr_vbn = ndm->last_wr_pbn = (ui32)-1;
1146     ndm->last_rd_vbn = ndm->last_rd_pbn = (ui32)-1;
1147 
1148     // Install driver callback routine function pointers.
1149     ndm->write_page = dvr->write_data_and_spare;
1150     ndm->read_page = dvr->read_decode_data;
1151     ndm->read_decode_spare = dvr->read_decode_spare;
1152     ndm->read_spare = dvr->read_spare;
1153     ndm->page_blank = dvr->data_and_spare_erased;
1154 #if INC_FTL_NDM
1155     ndm->check_page = dvr->data_and_spare_check;
1156 #endif
1157     ndm->erase_block = dvr->erase_block;
1158     ndm->is_block_bad = dvr->is_block_bad;
1159     ndm->dev = dvr->dev;
1160 
1161 #if INC_FFS_NDM_MLC || INC_FTL_NDM_MLC
1162     // The 'pair_offset' driver function does not take a page/address.
1163     ndm->pair_offset = dvr->pair_offset;
1164 #endif
1165 
1166     // If driver supplies transfer page function, use it.
1167     if (FLAG_IS_SET(dvr->flags, FSF_TRANSFER_PAGE)) {
1168         ndm->dev_ndm = ndm->dev;
1169         ndm->xfr_page = dvr->transfer_page;
1170     }
1171 
1172     // Else use internal read-page/write-page substitute.
1173     else {
1174         ndm->dev_ndm = ndm;
1175         ndm->xfr_page = ndm_xfr_page;
1176     }
1177 
1178     // If driver read/write pages supplied, use them directly.
1179     if (FLAG_IS_SET(dvr->flags, FSF_MULTI_ACCESS)) {
1180         ndm->read_pages = dvr->read_pages;
1181         ndm->write_pages = dvr->write_pages;
1182     }
1183 
1184     // Set the device type.
1185     ndm->dev_type = dvr->type;
1186 
1187     // Initialize the NDM.
1188     if (init_ndm(ndm))
1189         goto ndmAddDev_err;
1190 
1191 #if NV_NDM_CTRL_STORE
1192     // Account for NVRAM routine use.
1193     ++NdmDevCnt;
1194 #endif
1195 
1196     // Release exclusive access to NDM and return success.
1197     semPostBin(NdmSem);
1198     return ndm;
1199 
1200 // Error exit.
1201 ndmAddDev_err:
1202     CIRC_NODE_REMOVE(&ndm->link);
1203     if (ndm->init_bad_blk)
1204         FsFree(ndm->init_bad_blk);
1205     if (ndm->run_bad_blk)
1206         FsFree(ndm->run_bad_blk);
1207     if (ndm->sem)
1208         semDelete(&ndm->sem);
1209     if (ndm->main_buf)
1210         FsAfreeClear(&ndm->main_buf);
1211     if (ndm->partitions)
1212         FsFree(ndm->partitions);
1213     FsFree(ndm);
1214     semPostBin(NdmSem);
1215     return NULL;
1216 }
1217 
1218 //   ndmDelDev: Delete (uninitialize) the NDM
1219 //
1220 //       Input: ndm = pointer to NDM control block
1221 //
1222 //     Returns: 0 on success, -1 on failure
1223 //
ndmDelDev(NDM ndm)1224 int ndmDelDev(NDM ndm) {
1225     CircLink* circ;
1226     int saved_errno;
1227 
1228     // Acquire exclusive access to global NDM semaphore.
1229     semPend(NdmSem, WAIT_FOREVER);
1230 
1231     // Ensure the device is on the device list.
1232     for (circ = CIRC_LIST_HEAD(&NdmDevs);; circ = circ->next_bck) {
1233         // If the device was not found, return error.
1234         if (CIRC_LIST_AT_END(circ, &NdmDevs)) {
1235             semPostBin(NdmSem);
1236             return FsError(ENOENT);
1237         }
1238 
1239         // If device found, stop looking.
1240         if (ndm == (void*)circ)
1241             break;
1242     }
1243 
1244     // Remove device from list of devices.
1245     CIRC_NODE_REMOVE(&ndm->link);
1246 
1247     // Release exclusive access to global NDM semaphore.
1248     semPostBin(NdmSem);
1249 
1250     // Remove all volumes from device.
1251     saved_errno = errno;
1252     (void)ndmDelVols(ndm);
1253     errno = saved_errno;
1254 
1255     // Free the initial and running bad block maps.
1256     FsFree(ndm->init_bad_blk);
1257     FsFree(ndm->run_bad_blk);
1258 
1259     // Free the partitions table.
1260     if (ndm->partitions)
1261         FsFree(ndm->partitions);
1262 
1263     // Free the region semaphore, temporary space, and control block.
1264     semDelete(&ndm->sem);
1265     FsAfreeClear(&ndm->main_buf);
1266     FsFree(ndm);
1267 
1268 #if NV_NDM_CTRL_STORE
1269     // Decrement NDM device count.
1270     --NdmDevCnt;
1271 #endif
1272 
1273     // Return success.
1274     return 0;
1275 }
1276 
1277 // ndmInitBadBlock: Check if a block is in initial bad block map
1278 //
1279 //      Inputs: ndm = pointer to NDM control block
1280 //              b = block to check
1281 //
1282 //     Returns: TRUE iff initial bad block, FALSE otherwise
1283 //
ndmInitBadBlock(CNDM ndm,ui32 b)1284 int ndmInitBadBlock(CNDM ndm, ui32 b) {
1285     ui32 i;
1286 
1287     // Loop over list of initially bad blocks.
1288     for (i = 0; i <= ndm->max_bad_blks; ++i) {
1289         // If end of map, stop.
1290         if (ndm->init_bad_blk[i] == ndm->num_dev_blks)
1291             break;
1292 
1293         // If block found in map, it's initial bad block.
1294         if (ndm->init_bad_blk[i] == b)
1295             return TRUE;
1296     }
1297 
1298     // Return FALSE. Block is not an initial bad block.
1299     return FALSE;
1300 }
1301 
1302 #if RDBACK_CHECK
1303 //   ndmCkMeta: Read-back verify the TargetNDM metadata
1304 //
1305 //       Input: ndm0 = pointer to NDM control block
1306 //
ndmCkMeta(NDM ndm0)1307 void ndmCkMeta(NDM ndm0) {
1308     int rc;
1309     NDM ndm1;
1310 
1311     // Allocate TargetNDM control block for metadata comparison.
1312     ndm1 = FsCalloc(1, sizeof(struct ndm));
1313     if (ndm1 == NULL)
1314         exit(-1);
1315 
1316     // Copy initialized (not read) structure values.
1317     memcpy(ndm1, ndm0, sizeof(struct ndm));
1318 
1319     // Read the latest control info into temporary control block, using
1320     // - if any - previously allocated partition memory.
1321     rc = read_ctrl_info(ndm1);
1322     if (rc)
1323         exit(-1);
1324 
1325     // Compare control block used for writing with one used for reading.
1326     rc = memcmp(ndm1, ndm0, sizeof(struct ndm));
1327     if (rc)
1328         exit(-1);
1329 
1330     // Free allocated TargetNDM test control block.
1331     free(ndm1);
1332 }
1333 #endif // RDBACK_CHECK
1334 #endif // INC_NDM
1335