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 "aml-bad-block.h" 6 7#include <stdlib.h> 8 9#ifdef TEST 10#include <unittest/unittest.h> 11#define zxlogf(flags, ...) unittest_printf(__VA_ARGS__) 12#else 13#include <ddk/debug.h> 14#endif 15#include <ddk/protocol/nand.h> 16 17#include <fbl/algorithm.h> 18#include <fbl/alloc_checker.h> 19#include <fbl/auto_lock.h> 20#include <lib/sync/completion.h> 21 22namespace nand { 23 24namespace { 25 26constexpr uint32_t kBadBlockTableMagic = 0x7462626E; // "nbbt" 27 28struct BlockOperationContext { 29 sync_completion_t* completion_event; 30 zx_status_t status; 31}; 32 33void CompletionCallback(nand_op_t* op, zx_status_t status) { 34 auto* ctx = static_cast<BlockOperationContext*>(op->cookie); 35 36 zxlogf(TRACE, "Completion status: %d\n", status); 37 ctx->status = status; 38 sync_completion_signal(ctx->completion_event); 39 return; 40} 41 42} // namespace 43 44zx_status_t AmlBadBlock::Create(Config config, fbl::RefPtr<BadBlock>* out) { 45 // Query parent to get its nand_info_t and size for nand_op_t. 46 nand_info_t nand_info; 47 size_t parent_op_size; 48 config.nand_proto.ops->query(config.nand_proto.ctx, &nand_info, &parent_op_size); 49 50 // Allocate nand_op. 51 fbl::AllocChecker ac; 52 fbl::Array<uint8_t> nand_op(new (&ac) uint8_t[parent_op_size], parent_op_size); 53 if (!ac.check()) { 54 return ZX_ERR_NO_MEMORY; 55 } 56 57 // Allocate VMOs. 58 const uint32_t table_len = fbl::round_up(nand_info.num_blocks, nand_info.page_size); 59 zx::vmo data_vmo; 60 zx_status_t status = zx::vmo::create(table_len, 0, &data_vmo); 61 if (status != ZX_OK) { 62 zxlogf(ERROR, "nandpart: Failed to create VMO for bad block table\n"); 63 return status; 64 } 65 66 const uint32_t bbt_page_count = table_len / nand_info.page_size; 67 zx::vmo oob_vmo; 68 status = zx::vmo::create(sizeof(OobMetadata) * bbt_page_count, 0, &oob_vmo); 69 if (status != ZX_OK) { 70 zxlogf(ERROR, "nandpart: Failed to create VMO for oob metadata\n"); 71 return status; 72 } 73 74 // Map them. 75 constexpr uint32_t kPermissions = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE; 76 uintptr_t vaddr_table; 77 status = zx::vmar::root_self()->map(0, data_vmo, 0, table_len, kPermissions, &vaddr_table); 78 if (status != ZX_OK) { 79 zxlogf(ERROR, "nandpart: Failed to map VMO for bad block table\n"); 80 return status; 81 } 82 83 uintptr_t vaddr_oob; 84 status = zx::vmar::root_self()->map(0, oob_vmo, 0, sizeof(OobMetadata) * bbt_page_count, 85 kPermissions, &vaddr_oob); 86 if (status != ZX_OK) { 87 zxlogf(ERROR, "nandpart: Failed to map VMO for oob metadata\n"); 88 return status; 89 } 90 91 // Construct all the things. 92 *out = fbl::MakeRefCountedChecked<AmlBadBlock>(&ac, fbl::move(data_vmo), fbl::move(oob_vmo), 93 fbl::move(nand_op), config, nand_info, 94 reinterpret_cast<BlockStatus*>(vaddr_table), 95 table_len, 96 reinterpret_cast<OobMetadata*>(vaddr_oob)); 97 if (!ac.check()) { 98 return ZX_ERR_NO_MEMORY; 99 } 100 return ZX_OK; 101} 102 103zx_status_t AmlBadBlock::EraseBlock(uint32_t block) { 104 sync_completion_t completion; 105 BlockOperationContext op_ctx = {.completion_event = &completion, 106 .status = ZX_ERR_INTERNAL}; 107 auto* nand_op = reinterpret_cast<nand_op_t*>(nand_op_.get()); 108 nand_op->erase.command = NAND_OP_ERASE; 109 nand_op->erase.first_block = block; 110 nand_op->erase.num_blocks = 1; 111 nand_op->completion_cb = CompletionCallback; 112 nand_op->cookie = &op_ctx; 113 nand_.Queue(nand_op); 114 115 // Wait on completion. 116 sync_completion_wait(&completion, ZX_TIME_INFINITE); 117 return op_ctx.status; 118} 119 120zx_status_t AmlBadBlock::GetNewBlock() { 121 for (;;) { 122 // Find a block with the least number of PE cycles. 123 uint16_t least_pe_cycles = UINT16_MAX; 124 uint32_t index = kBlockListMax; 125 for (uint32_t i = 0; i < kBlockListMax; i++) { 126 if (block_list_[i].valid && 127 &block_list_[i] != block_entry_ && 128 block_list_[i].program_erase_cycles < least_pe_cycles) { 129 least_pe_cycles = block_list_[i].program_erase_cycles; 130 index = i; 131 } 132 } 133 if (index == kBlockListMax) { 134 zxlogf(ERROR, "nandpart: Unable to find a valid block to store BBT into\n"); 135 return ZX_ERR_NOT_FOUND; 136 } 137 138 // Make sure we aren't trying to write to a bad block. 139 const uint32_t block = block_list_[index].block; 140 if (bad_block_table_[block] != kNandBlockGood) { 141 // Try again. 142 block_list_[index].valid = false; 143 continue; 144 } 145 146 // Erase the block before using it. 147 const zx_status_t status = EraseBlock(block); 148 if (status != ZX_OK) { 149 zxlogf(ERROR, "nandpart: Failed to erase block %u, marking bad\n", block); 150 // Mark the block as bad and try again. 151 bad_block_table_[block] = kNandBlockBad; 152 block_list_[index].valid = false; 153 continue; 154 } 155 156 zxlogf(INFO, "nandpart: Moving BBT to block %u\n", block); 157 block_entry_ = &block_list_[index]; 158 block_list_[index].program_erase_cycles++; 159 page_ = 0; 160 return ZX_OK; 161 } 162} 163 164zx_status_t AmlBadBlock::WritePages(uint32_t nand_page, uint32_t num_pages) { 165 sync_completion_t completion; 166 BlockOperationContext op_ctx = {.completion_event = &completion, 167 .status = ZX_ERR_INTERNAL}; 168 169 auto* nand_op = reinterpret_cast<nand_op_t*>(nand_op_.get()); 170 nand_op->rw.command = NAND_OP_WRITE; 171 nand_op->rw.data_vmo = data_vmo_.get(); 172 nand_op->rw.oob_vmo = oob_vmo_.get(); 173 nand_op->rw.length = num_pages; 174 nand_op->rw.offset_nand = nand_page; 175 nand_op->rw.offset_data_vmo = 0; 176 nand_op->rw.offset_oob_vmo = 0; 177 nand_op->completion_cb = CompletionCallback; 178 nand_op->cookie = &op_ctx; 179 nand_.Queue(nand_op); 180 181 // Wait on completion. 182 sync_completion_wait(&completion, ZX_TIME_INFINITE); 183 return op_ctx.status; 184} 185 186zx_status_t AmlBadBlock::WriteBadBlockTable(bool use_new_block) { 187 ZX_DEBUG_ASSERT(bad_block_table_len_ % nand_info_.page_size == 0); 188 const uint32_t bbt_page_count = bad_block_table_len_ / nand_info_.page_size; 189 190 for (;;) { 191 if (use_new_block || 192 bad_block_table_[block_entry_->block] != kNandBlockGood || 193 page_ + bbt_page_count >= nand_info_.pages_per_block) { 194 // Current BBT is in a bad block, or it is full, so we must find a new one. 195 use_new_block = false; 196 zxlogf(INFO, "nandpart: Finding a new block to store BBT into\n"); 197 const zx_status_t status = GetNewBlock(); 198 if (status != ZX_OK) { 199 return status; 200 } 201 } 202 203 // Perform write. 204 for (auto* oob = oob_; oob < oob_ + bbt_page_count; oob++) { 205 oob->magic = kBadBlockTableMagic; 206 oob->program_erase_cycles = block_entry_->program_erase_cycles; 207 oob->generation = generation_; 208 } 209 210 const uint32_t block = block_entry_->block; 211 const uint32_t nand_page = (block * nand_info_.pages_per_block) + page_; 212 const zx_status_t status = WritePages(nand_page, bbt_page_count); 213 if (status != ZX_OK) { 214 zxlogf(ERROR, "nandpart: BBT write failed. Marking %u bad and trying again\n", 215 block); 216 bad_block_table_[block] = kNandBlockBad; 217 continue; 218 } 219 zxlogf(TRACE, "nandpart: BBT write to block %u pages [%u, %u) successful\n", block, 220 page_, page_ + bbt_page_count); 221 break; 222 } 223 224 page_ += bbt_page_count; 225 generation_++; 226 return ZX_OK; 227} 228 229zx_status_t AmlBadBlock::ReadPages(uint32_t nand_page, uint32_t num_pages) { 230 sync_completion_t completion; 231 BlockOperationContext op_ctx = {.completion_event = &completion, 232 .status = ZX_ERR_INTERNAL}; 233 auto* nand_op = reinterpret_cast<nand_op_t*>(nand_op_.get()); 234 nand_op->rw.command = NAND_OP_READ; 235 nand_op->rw.data_vmo = data_vmo_.get(); 236 nand_op->rw.oob_vmo = oob_vmo_.get(); 237 nand_op->rw.length = num_pages; 238 nand_op->rw.offset_nand = nand_page; 239 nand_op->rw.offset_data_vmo = 0; 240 nand_op->rw.offset_oob_vmo = 0; 241 nand_op->completion_cb = CompletionCallback; 242 nand_op->cookie = &op_ctx; 243 nand_.Queue(nand_op); 244 245 // Wait on completion. 246 sync_completion_wait(&completion, ZX_TIME_INFINITE); 247 return op_ctx.status; 248} 249 250zx_status_t AmlBadBlock::FindBadBlockTable() { 251 zxlogf(TRACE, "nandpart: Finding bad block table\n"); 252 253 if (sizeof(OobMetadata) > nand_info_.oob_size) { 254 zxlogf(ERROR, "nandpart: OOB is too small. Need %zu, found %u\n", sizeof(OobMetadata), 255 nand_info_.oob_size); 256 return ZX_ERR_NOT_SUPPORTED; 257 } 258 259 zxlogf(TRACE, "nandpart: Starting in block %u. Ending in block %u.\n", 260 config_.aml.table_start_block, config_.aml.table_end_block); 261 262 const uint32_t blocks = config_.aml.table_end_block - config_.aml.table_start_block; 263 if (blocks == 0 || blocks > kBlockListMax) { 264 // Driver assumption that no more than |kBlockListMax| blocks will be dedicated for BBT use. 265 zxlogf(ERROR, "Unsupported number of blocks used for BBT.\n"); 266 return ZX_ERR_NOT_SUPPORTED; 267 } 268 269 // First find the block the BBT lives in. 270 ZX_DEBUG_ASSERT(bad_block_table_len_ % nand_info_.page_size == 0); 271 const uint32_t bbt_page_count = bad_block_table_len_ / nand_info_.page_size; 272 273 int8_t valid_blocks = 0; 274 block_entry_ = NULL; 275 uint32_t block = config_.aml.table_start_block; 276 for (; block <= config_.aml.table_end_block; block++) { 277 // Attempt to read up to 6 entries to see if block is valid. 278 uint32_t nand_page = block * nand_info_.pages_per_block; 279 zx_status_t status = ZX_ERR_INTERNAL; 280 for (uint32_t i = 0; i < 6 && status != ZX_OK; i++, nand_page += bbt_page_count) { 281 status = ReadPages(nand_page, 1); 282 } 283 if (status != ZX_OK) { 284 // This block is untrustworthy. Do not add it to the block list. 285 // TODO(surajmalhotra): Should we somehow mark this block as bad or 286 // try erasing it? 287 zxlogf(ERROR, "nandpart: Unable to read any pages in block %u\n", block); 288 continue; 289 } 290 291 zxlogf(TRACE, "Successfully read block %u.\n", block); 292 293 block_list_[valid_blocks].block = block; 294 block_list_[valid_blocks].valid = true; 295 296 // If block has valid BBT entries, see if it has the latest entries. 297 if (oob_->magic == kBadBlockTableMagic) { 298 if (oob_->generation >= generation_) { 299 zxlogf(TRACE, "Block %u has valid BBT entries!\n", block); 300 block_entry_ = &block_list_[valid_blocks]; 301 generation_ = oob_->generation; 302 } 303 block_list_[valid_blocks].program_erase_cycles = oob_->program_erase_cycles; 304 } else if (oob_->magic == 0xFFFFFFFF) { 305 // Page is erased. 306 block_list_[valid_blocks].program_erase_cycles = 0; 307 } else { 308 zxlogf(ERROR, "Block %u is neither erased, nor contains a valid entry!\n", block); 309 block_list_[valid_blocks].program_erase_cycles = oob_->program_erase_cycles; 310 } 311 312 valid_blocks++; 313 } 314 315 if (block_entry_ == NULL) { 316 zxlogf(ERROR, "nandpart: No valid BBT entries found!\n"); 317 // TODO(surajmalhotra): Initialize the BBT by reading the factory bad 318 // blocks. 319 return ZX_ERR_INTERNAL; 320 } 321 322 for (size_t idx = valid_blocks - 1; idx < kBlockListMax; idx++) { 323 block_list_[idx].valid = false; 324 } 325 326 zxlogf(TRACE, "nandpart: Finding last BBT in block %u\n", block_entry_->block); 327 328 // Next find the last valid BBT entry in block. 329 bool found_one = false; 330 bool latest_entry_bad = true; 331 uint32_t page = 0; 332 bool break_loop = false; 333 for (; page + bbt_page_count <= nand_info_.pages_per_block; page += bbt_page_count) { 334 zx_status_t status = ZX_OK; 335 // Check that all pages in current bbt_page_count are valid. 336 zxlogf(TRACE, "Reading pages [%u, %u)\n", page, page + bbt_page_count); 337 const uint32_t nand_page = block_entry_->block * nand_info_.pages_per_block + page; 338 status = ReadPages(nand_page, bbt_page_count); 339 if (status != ZX_OK) { 340 // It's fine for entries to be unreadable as long as future ones are 341 // readable. 342 zxlogf(TRACE, "nandpart: Unable to read page %u\n", page); 343 latest_entry_bad = true; 344 continue; 345 } 346 for (uint32_t i = 0; i < bbt_page_count; i++) { 347 if ((oob_ + i)->magic != kBadBlockTableMagic) { 348 // Last BBT entry in table was found, so quit looking at future entries. 349 zxlogf(TRACE, "nandpart: Page %u does not contain valid BBT entry\n", page + i); 350 break_loop = true; 351 break; 352 } 353 } 354 if (break_loop) { 355 break; 356 } 357 // Store latest complete BBT. 358 zxlogf(TRACE, "BBT entry in pages (%u, %u] is valid\n", page, page + bbt_page_count); 359 latest_entry_bad = false; 360 found_one = true; 361 page_ = page; 362 generation_ = static_cast<uint16_t>(oob_->generation + 1); 363 } 364 365 if (!found_one) { 366 zxlogf(ERROR, "nandpart: Unable to find a valid copy of the bad block table\n"); 367 return ZX_ERR_NOT_FOUND; 368 } 369 370 if (page + bbt_page_count <= nand_info_.pages_per_block || latest_entry_bad) { 371 // Last iteration failed to read valid copy of BBT (that's how loop exited), 372 // so we need to reread the BBT. 373 const uint32_t nand_page = block_entry_->block * nand_info_.pages_per_block + page_; 374 const zx_status_t status = ReadPages(nand_page, bbt_page_count); 375 if (status != ZX_OK) { 376 zxlogf(ERROR, "nandpart: Unable to re-read latest copy of bad block table\n"); 377 return status; 378 } 379 for (uint32_t i = 0; i < bbt_page_count; i++) { 380 if ((oob_ + i)->magic != kBadBlockTableMagic) { 381 zxlogf(ERROR, "nandpart: Latest copy of bad block table no longer valid?\n"); 382 return ZX_ERR_INTERNAL; 383 } 384 } 385 386 if (latest_entry_bad) { 387 zxlogf(ERROR, "nandpart: Latest entry in block %u is invalid. Moving bad block file.\n", 388 block_entry_->block); 389 constexpr bool kUseNewBlock = true; 390 const zx_status_t status = WriteBadBlockTable(kUseNewBlock); 391 if (status != ZX_OK) { 392 return status; 393 } 394 } else { 395 // Page needs to point to next available slot. 396 zxlogf(INFO, "nandpart: Latest BBT entry found in pages [%u, %u)\n", page_, 397 page + bbt_page_count); 398 page_ += bbt_page_count; 399 } 400 } 401 402 table_valid_ = true; 403 return ZX_OK; 404} 405 406zx_status_t AmlBadBlock::GetBadBlockList(uint32_t first_block, uint32_t last_block, 407 fbl::Array<uint32_t>* bad_blocks) { 408 fbl::AutoLock al(&lock_); 409 if (!table_valid_) { 410 const zx_status_t status = FindBadBlockTable(); 411 if (status != ZX_OK) { 412 return status; 413 } 414 } 415 416 if (first_block >= nand_info_.num_blocks || last_block > nand_info_.num_blocks) { 417 return ZX_ERR_INVALID_ARGS; 418 } 419 420 // Scan BBT for bad block list. 421 size_t bad_block_count = 0; 422 for (uint32_t block = first_block; block < last_block; block++) { 423 if (bad_block_table_[block] != kNandBlockGood) { 424 bad_block_count += 1; 425 } 426 } 427 428 // Early return if no bad blocks found. 429 if (bad_block_count == 0) { 430 bad_blocks->reset(); 431 return ZX_OK; 432 } 433 434 // Allocate array and copy list. 435 fbl::AllocChecker ac; 436 bad_blocks->reset(new (&ac) uint32_t[bad_block_count], bad_block_count); 437 if (!ac.check()) { 438 return ZX_ERR_NO_MEMORY; 439 } 440 441 bad_block_count = 0; 442 for (uint32_t block = first_block; block < last_block; block++) { 443 if (bad_block_table_[block] != kNandBlockGood) { 444 (*bad_blocks)[bad_block_count++] = block; 445 } 446 } 447 448 return ZX_OK; 449} 450 451zx_status_t AmlBadBlock::MarkBlockBad(uint32_t block) { 452 fbl::AutoLock al(&lock_); 453 if (!table_valid_) { 454 const zx_status_t status = FindBadBlockTable(); 455 if (status != ZX_OK) { 456 return status; 457 } 458 } 459 460 if (block > nand_info_.num_blocks) { 461 return ZX_ERR_OUT_OF_RANGE; 462 } 463 464 // Early return if block is already marked bad. 465 if (bad_block_table_[block] != kNandBlockGood) { 466 return ZX_OK; 467 } 468 bad_block_table_[block] = kNandBlockBad; 469 470 constexpr bool kNoUseNewBlock = false; 471 return WriteBadBlockTable(kNoUseNewBlock); 472} 473 474} // namespace nand 475