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