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 <fcntl.h> 6#include <getopt.h> 7#include <stdio.h> 8#include <stdlib.h> 9 10#include <fbl/algorithm.h> 11#include <fbl/unique_fd.h> 12#include <lib/fzl/mapped-vmo.h> 13#include <lib/cksum.h> 14#include <pretty/hexdump.h> 15#include <zircon/assert.h> 16#include <zircon/device/nand.h> 17#include <zircon/device/nand-broker.h> 18#include <zircon/status.h> 19#include <zircon/syscalls.h> 20#include <zxcpp/new.h> 21 22#include "aml.h" 23 24namespace { 25 26constexpr char kUsageMessage[] = R"""( 27Low level access tool for a NAND device. 28WARNING: This tool may overwrite the NAND device. 29 30./nand-util --device /dev/sys/platform/05:00:d/aml-raw_nand/nand/broker --info 31 32Note that to use this tool the driver binding rules have to be adjusted so that 33the broker driver is loaded for the desired NAND device. 34 35Options: 36 --device (-d) path : Specifies the broker device to use. 37 --info (-i) : Show basic NAND information. 38 --bbt (-t) : Display bad block info. 39 --read (-r) --absolute xxx : Read the page number xxx (0-based). 40 --erase (-e) --block xxx : Erase the block number xxx (0-based). 41 --check (-c) : Looks for read errors on the device. 42 --absolute (-a) xxx : Use an absolute page number. 43 --page (-p) xxx : Use the xxx page number (from within a block). 44 --block (-b) xxx : Use the xxx block number. 45 --count (-n) xxx : Limit the operation to xxx blocks. 46 Only supported with --check. 47)"""; 48 49// Configuration info (what to do). 50struct Config { 51 const char* path; 52 uint32_t page_num; 53 uint32_t block_num; 54 uint32_t abs_page; 55 uint32_t count; 56 int actions; 57 bool info; 58 bool bbt; 59 bool read; 60 bool erase; 61 bool read_check; 62}; 63 64// Broker device wrapper. 65class NandBroker { 66 public: 67 explicit NandBroker(const char* path) : device_(open(path, O_RDWR)) {} 68 ~NandBroker() {} 69 70 // Returns true on success. 71 bool Initialize(); 72 73 // Returns a file descriptor for the device. 74 int get() const { return device_.get(); } 75 76 // The internal buffer can access a block at a time. 77 const char* data() const { return reinterpret_cast<char*>(vmo_->GetData()); } 78 const char* oob() const { return data() + info_.page_size * info_.pages_per_block; } 79 80 const nand_info_t& Info() const { return info_; } 81 82 // The operations to perform: 83 bool Query(); 84 void ShowInfo() const; 85 bool ReadPages(uint32_t first_page, uint32_t count) const; 86 bool DumpPage(uint32_t page) const; 87 bool EraseBlock(uint32_t block) const; 88 89 private: 90 fbl::unique_fd device_; 91 nand_info_t info_ = {}; 92 fbl::unique_ptr<fzl::MappedVmo> vmo_; 93}; 94 95bool NandBroker::Initialize() { 96 if (!Query()) { 97 printf("Failed to open or query the device\n"); 98 return false; 99 } 100 const uint32_t size = (info_.page_size + info_.oob_size) * info_.pages_per_block; 101 if (fzl::MappedVmo::Create(size, nullptr, &vmo_) != ZX_OK) { 102 printf("Failed to allocate VMO\n"); 103 return false; 104 } 105 return true; 106} 107 108bool NandBroker::Query() { 109 if (!device_) { 110 return false; 111 } 112 113 return ioctl_nand_broker_get_info(device_.get(), &info_) == sizeof(info_); 114} 115 116void NandBroker::ShowInfo() const { 117 printf("Page size: %d\nPages per block: %d\nTotal Blocks: %d\nOOB size: %d\nECC bits: %d\n" 118 "Nand class: %d\n", info_.page_size, info_.pages_per_block, info_.num_blocks, 119 info_.oob_size, info_.ecc_bits, info_.nand_class); 120} 121 122bool NandBroker::ReadPages(uint32_t first_page, uint32_t count) const { 123 ZX_DEBUG_ASSERT(count <= info_.pages_per_block); 124 nand_broker_request_t request = {}; 125 nand_broker_response_t response = {}; 126 127 request.length = count; 128 request.offset_nand = first_page; 129 request.offset_oob_vmo = info_.pages_per_block; // OOB is at the end of the VMO. 130 request.data_vmo = true; 131 request.oob_vmo = true; 132 133 if (zx_handle_duplicate(vmo_->GetVmo(), ZX_RIGHT_SAME_RIGHTS, &request.vmo) != ZX_OK) { 134 printf("Failed to duplicate VMO\n"); 135 return false; 136 } 137 138 if (ioctl_nand_broker_read(get(), &request, &response) != sizeof(response)) { 139 printf("Failed to issue command to driver\n"); 140 return false; 141 } 142 143 if (response.status != ZX_OK) { 144 printf("Read to %d pages starting at %d failed with %s\n", count, first_page, 145 zx_status_get_string(response.status)); 146 return false; 147 } 148 149 if (response.corrected_bit_flips > info_.ecc_bits) { 150 printf("Read to %d pages starting at %d unable to correct all bit flips\n", count, 151 first_page); 152 } else if (response.corrected_bit_flips) { 153 // If the nand protocol is modified to provide more info, we could display something 154 // like average bit flips. 155 printf("Read to %d pages starting at %d corrected %d errors\n", count, first_page, 156 response.corrected_bit_flips); 157 } 158 159 return true; 160} 161 162bool NandBroker::DumpPage(uint32_t page) const { 163 if (!ReadPages(page, 1)) { 164 return false; 165 } 166 ZX_DEBUG_ASSERT(info_.page_size % 16 == 0); 167 168 uint32_t address = page * info_.page_size; 169 hexdump8_ex(data(), 16, address); 170 int skip = 0; 171 172 for (uint32_t line = 16; line < info_.page_size; line += 16) { 173 if (memcmp(data() + line, data() + line - 16, 16) == 0) { 174 skip++; 175 if (skip < 50) { 176 printf("."); 177 } 178 continue; 179 } 180 if (skip) { 181 printf("\n"); 182 skip = 0; 183 } 184 hexdump8_ex(data() + line, 16, address + line); 185 } 186 187 if (skip) { 188 printf("\n"); 189 } 190 191 printf("OOB:\n"); 192 hexdump8_ex(oob(), info_.oob_size, address + info_.page_size); 193 return true; 194} 195 196bool NandBroker::EraseBlock(uint32_t block) const { 197 nand_broker_request_t request = {}; 198 nand_broker_response_t response = {}; 199 200 request.length = 1; 201 request.offset_nand = block; 202 203 if (ioctl_nand_broker_erase(get(), &request, &response) != sizeof(response)) { 204 printf("Failed to issue command to driver\n"); 205 return false; 206 } 207 208 if (response.status != ZX_OK) { 209 printf("Erase block %d failed with %s\n", block, zx_status_get_string(response.status)); 210 return false; 211 } 212 213 return true; 214} 215 216bool GetOptions(int argc, char** argv, Config* config) { 217 while (true) { 218 struct option options[] = { 219 {"device", required_argument, nullptr, 'd'}, 220 {"info", no_argument, nullptr, 'i'}, 221 {"bbt", no_argument, nullptr, 't'}, 222 {"read", no_argument, nullptr, 'r'}, 223 {"erase", no_argument, nullptr, 'e'}, 224 {"check", no_argument, nullptr, 'c'}, 225 {"page", required_argument, nullptr, 'p'}, 226 {"block", required_argument, nullptr, 'b'}, 227 {"absolute", required_argument, nullptr, 'a'}, 228 {"count", required_argument, nullptr, 'n'}, 229 {"help", no_argument, nullptr, 'h'}, 230 {nullptr, 0, nullptr, 0}, 231 }; 232 int opt_index; 233 int c = getopt_long(argc, argv, "d:irtecp:b:a:n:h", options, &opt_index); 234 if (c < 0) { 235 break; 236 } 237 switch (c) { 238 case 'd': 239 config->path = optarg; 240 break; 241 case 'i': 242 config->info = true; 243 break; 244 case 't': 245 config->bbt = true; 246 config->actions++; 247 break; 248 case 'r': 249 config->read = true; 250 config->actions++; 251 break; 252 case 'e': 253 config->erase = true; 254 config->actions++; 255 break; 256 case 'c': 257 config->read_check = true; 258 config->actions++; 259 break; 260 case 'p': 261 config->page_num = static_cast<uint32_t>(strtoul(optarg, NULL, 0)); 262 break; 263 case 'b': 264 config->block_num = static_cast<uint32_t>(strtoul(optarg, NULL, 0)); 265 break; 266 case 'a': 267 config->abs_page = static_cast<uint32_t>(strtoul(optarg, NULL, 0)); 268 break; 269 case 'n': 270 config->count = static_cast<uint32_t>(strtoul(optarg, NULL, 0)); 271 break; 272 case 'h': 273 printf("%s\n", kUsageMessage); 274 return 0; 275 } 276 } 277 return argc == optind; 278} 279 280bool ValidateOptions(const Config& config) { 281 if (!config.path) { 282 printf("Device needed\n"); 283 printf("%s\n", kUsageMessage); 284 return false; 285 } 286 287 if (config.actions > 1) { 288 printf("Only one action allowed\n"); 289 return false; 290 } 291 292 if (config.abs_page && config.page_num) { 293 printf("Provide either a block + page or an absolute page number\n"); 294 return false; 295 } 296 297 if (config.erase && (config.page_num || config.abs_page)) { 298 printf("Erase works with blocks, not pages\n"); 299 return false; 300 } 301 302 if (config.erase && config.block_num < 24) { 303 printf("Erasing the restricted area is not a good idea, sorry\n"); 304 return false; 305 } 306 307 if (!config.info && !config.actions) { 308 printf("Nothing to do\n"); 309 return false; 310 } 311 312 if (config.count && !config.read_check) { 313 printf("Count only supported for --check\n"); 314 return false; 315 } 316 return true; 317} 318 319bool ValidateOptionsWithNand(const NandBroker& nand, const Config& config) { 320 if (config.page_num >= nand.Info().pages_per_block) { 321 printf("Page not within a block:\n"); 322 return false; 323 } 324 325 if (config.block_num >= nand.Info().num_blocks) { 326 printf("Block not within device:\n"); 327 return false; 328 } 329 330 if (config.abs_page >= nand.Info().num_blocks * nand.Info().pages_per_block) { 331 printf("Page not within device:\n"); 332 return false; 333 } 334 335 return true; 336} 337 338bool FindBadBlocks(const NandBroker& nand) { 339 if (!nand.ReadPages(0, 1)) { 340 return false; 341 } 342 343 uint32_t first_block; 344 uint32_t num_blocks; 345 GetBbtLocation(nand.data(), &first_block, &num_blocks); 346 bool found = false; 347 for (uint32_t block = 0; block < num_blocks; block++) { 348 uint32_t start = (first_block + block) * nand.Info().pages_per_block; 349 if (!nand.ReadPages(start, nand.Info().pages_per_block)) { 350 return false; 351 } 352 if (!DumpBbt(nand.data(), nand.oob(), nand.Info())) { 353 break; 354 } 355 found = true; 356 } 357 if (!found) { 358 printf("Unable to find any table\n"); 359 } 360 return found; 361} 362 363// Verifies that reads always return the same data. 364bool ReadCheck(const NandBroker& nand, uint32_t first_block, uint32_t count) { 365 constexpr int kNumReads = 10; 366 uint32_t num_blocks = fbl::min(nand.Info().num_blocks, first_block + count); 367 size_t size = (nand.Info().page_size + nand.Info().oob_size) * nand.Info().pages_per_block; 368 for (uint32_t block = first_block; block < num_blocks; block++) { 369 uint32_t first_crc; 370 for (int i = 0; i < kNumReads; i++) { 371 const uint32_t start = block * nand.Info().pages_per_block; 372 if (!nand.ReadPages(start, nand.Info().pages_per_block)) { 373 printf("\nRead failed for block %u\n", block); 374 return false; 375 } 376 const uint32_t crc = crc32(0, reinterpret_cast<const uint8_t*>(nand.data()), size); 377 if (!i) { 378 first_crc = crc; 379 } else if (first_crc != crc) { 380 printf("\nMismatched reads on block %u\n", block); 381 return false; 382 } 383 } 384 printf("Block %u\r", block); 385 } 386 printf("\ndone\n"); 387 return true; 388} 389 390} // namespace 391 392int main(int argc, char** argv) { 393 Config config = {}; 394 if (!GetOptions(argc, argv, &config)) { 395 printf("%s\n", kUsageMessage); 396 return -1; 397 } 398 399 if (!ValidateOptions(config)) { 400 return -1; 401 } 402 403 NandBroker nand(config.path); 404 if (!nand.Initialize()) { 405 printf("Unable to open the nand device\n"); 406 return -1; 407 } 408 409 if (config.info) { 410 nand.ShowInfo(); 411 if (!nand.ReadPages(0, 1)) { 412 return -1; 413 } 414 DumpPage0(nand.data()); 415 } 416 417 if (config.bbt) { 418 return FindBadBlocks(nand) ? 0 : -1; 419 } 420 421 if (!ValidateOptionsWithNand(nand, config)) { 422 nand.ShowInfo(); 423 return -1; 424 } 425 426 if (config.read) { 427 if (!config.abs_page) { 428 config.abs_page = config.block_num * nand.Info().pages_per_block + config.page_num; 429 } 430 printf("To read page %d\n", config.abs_page); 431 return nand.DumpPage(config.abs_page) ? 0 : -1; 432 } 433 434 if (config.erase) { 435 printf("About to erase block %d. Press y to confirm\n", config.block_num); 436 if (getchar() != 'y') { 437 return -1; 438 } 439 return nand.EraseBlock(config.block_num) ? 0 : -1; 440 } 441 442 if (config.read_check) { 443 printf("Checking blocks...\n"); 444 return ReadCheck(nand, config.block_num, config.count) ? 0 : -1; 445 } 446 447 return 0; 448} 449