Dees_Troy | 51a0e82 | 2012-09-05 15:24:24 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2011 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include <errno.h> |
| 18 | #include <fcntl.h> |
| 19 | #include <stdio.h> |
| 20 | #include <stdlib.h> |
| 21 | #include <string.h> |
| 22 | #include <unistd.h> |
| 23 | #include <sys/ioctl.h> |
| 24 | #include <sys/types.h> |
| 25 | #include <sys/stat.h> |
| 26 | #include <limits.h> |
| 27 | |
| 28 | #include "cutils/log.h" |
| 29 | |
| 30 | #include <mtd/mtd-user.h> |
| 31 | |
| 32 | #include "mtdutils.h" |
| 33 | |
| 34 | typedef struct BmlOverMtdReadContext { |
| 35 | const MtdPartition *partition; |
| 36 | char *buffer; |
| 37 | size_t consumed; |
| 38 | int fd; |
| 39 | } BmlOverMtdReadContext; |
| 40 | |
| 41 | typedef struct BmlOverMtdWriteContext { |
| 42 | const MtdPartition *partition; |
| 43 | char *buffer; |
| 44 | size_t stored; |
| 45 | int fd; |
| 46 | |
| 47 | off_t* bad_block_offsets; |
| 48 | int bad_block_alloc; |
| 49 | int bad_block_count; |
| 50 | } BmlOverMtdWriteContext; |
| 51 | |
| 52 | |
| 53 | static BmlOverMtdReadContext *bml_over_mtd_read_partition(const MtdPartition *partition) |
| 54 | { |
| 55 | BmlOverMtdReadContext *ctx = (BmlOverMtdReadContext*) malloc(sizeof(BmlOverMtdReadContext)); |
| 56 | if (ctx == NULL) return NULL; |
| 57 | |
| 58 | ctx->buffer = malloc(partition->erase_size); |
| 59 | if (ctx->buffer == NULL) { |
| 60 | free(ctx); |
| 61 | return NULL; |
| 62 | } |
| 63 | |
| 64 | char mtddevname[32]; |
| 65 | sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index); |
| 66 | ctx->fd = open(mtddevname, O_RDONLY); |
| 67 | if (ctx->fd < 0) { |
| 68 | free(ctx); |
| 69 | free(ctx->buffer); |
| 70 | return NULL; |
| 71 | } |
| 72 | |
| 73 | ctx->partition = partition; |
| 74 | ctx->consumed = partition->erase_size; |
| 75 | return ctx; |
| 76 | } |
| 77 | |
| 78 | static void bml_over_mtd_read_close(BmlOverMtdReadContext *ctx) |
| 79 | { |
| 80 | close(ctx->fd); |
| 81 | free(ctx->buffer); |
| 82 | free(ctx); |
| 83 | } |
| 84 | |
| 85 | static BmlOverMtdWriteContext *bml_over_mtd_write_partition(const MtdPartition *partition) |
| 86 | { |
| 87 | BmlOverMtdWriteContext *ctx = (BmlOverMtdWriteContext*) malloc(sizeof(BmlOverMtdWriteContext)); |
| 88 | if (ctx == NULL) return NULL; |
| 89 | |
| 90 | ctx->bad_block_offsets = NULL; |
| 91 | ctx->bad_block_alloc = 0; |
| 92 | ctx->bad_block_count = 0; |
| 93 | |
| 94 | ctx->buffer = malloc(partition->erase_size); |
| 95 | if (ctx->buffer == NULL) { |
| 96 | free(ctx); |
| 97 | return NULL; |
| 98 | } |
| 99 | |
| 100 | char mtddevname[32]; |
| 101 | sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index); |
| 102 | ctx->fd = open(mtddevname, O_RDWR); |
| 103 | if (ctx->fd < 0) { |
| 104 | free(ctx->buffer); |
| 105 | free(ctx); |
| 106 | return NULL; |
| 107 | } |
| 108 | |
| 109 | ctx->partition = partition; |
| 110 | ctx->stored = 0; |
| 111 | return ctx; |
| 112 | } |
| 113 | |
| 114 | static int bml_over_mtd_write_close(BmlOverMtdWriteContext *ctx) |
| 115 | { |
| 116 | int r = 0; |
| 117 | if (close(ctx->fd)) r = -1; |
| 118 | free(ctx->bad_block_offsets); |
| 119 | free(ctx->buffer); |
| 120 | free(ctx); |
| 121 | return r; |
| 122 | } |
| 123 | |
| 124 | |
| 125 | #ifdef LOG_TAG |
| 126 | #undef LOG_TAG |
| 127 | #endif |
| 128 | |
| 129 | #define LOG_TAG "bml_over_mtd" |
| 130 | |
| 131 | #define BLOCK_SIZE 2048 |
| 132 | #define SPARE_SIZE (BLOCK_SIZE >> 5) |
| 133 | |
| 134 | #define EXIT_CODE_BAD_BLOCKS 15 |
| 135 | |
| 136 | static int die(const char *msg, ...) { |
| 137 | int err = errno; |
| 138 | va_list args; |
| 139 | va_start(args, msg); |
| 140 | char buf[1024]; |
| 141 | vsnprintf(buf, sizeof(buf), msg, args); |
| 142 | va_end(args); |
| 143 | |
| 144 | if (err != 0) { |
| 145 | strlcat(buf, ": ", sizeof(buf)); |
| 146 | strlcat(buf, strerror(err), sizeof(buf)); |
| 147 | } |
| 148 | |
| 149 | fprintf(stderr, "%s\n", buf); |
| 150 | return 1; |
| 151 | } |
| 152 | |
| 153 | static unsigned short* CreateEmptyBlockMapping(const MtdPartition* pSrcPart) |
| 154 | { |
| 155 | size_t srcTotal, srcErase, srcWrite; |
| 156 | if (mtd_partition_info(pSrcPart, &srcTotal, &srcErase, &srcWrite) != 0) |
| 157 | { |
| 158 | fprintf(stderr, "Failed to access partition.\n"); |
| 159 | return NULL; |
| 160 | } |
| 161 | |
| 162 | int numSrcBlocks = srcTotal/srcErase; |
| 163 | |
| 164 | unsigned short* pMapping = malloc(numSrcBlocks * sizeof(unsigned short)); |
| 165 | if (pMapping == NULL) |
| 166 | { |
| 167 | fprintf(stderr, "Failed to allocate block mapping memory.\n"); |
| 168 | return NULL; |
| 169 | } |
| 170 | memset(pMapping, 0xFF, numSrcBlocks * sizeof(unsigned short)); |
| 171 | return pMapping; |
| 172 | } |
| 173 | |
| 174 | static const unsigned short* CreateBlockMapping(const MtdPartition* pSrcPart, int srcPartStartBlock, |
| 175 | const MtdPartition *pReservoirPart, int reservoirPartStartBlock) |
| 176 | { |
| 177 | size_t srcTotal, srcErase, srcWrite; |
| 178 | if (mtd_partition_info(pSrcPart, &srcTotal, &srcErase, &srcWrite) != 0) |
| 179 | { |
| 180 | fprintf(stderr, "Failed to access partition.\n"); |
| 181 | return NULL; |
| 182 | } |
| 183 | |
| 184 | int numSrcBlocks = srcTotal/srcErase; |
| 185 | |
| 186 | unsigned short* pMapping = malloc(numSrcBlocks * sizeof(unsigned short)); |
| 187 | if (pMapping == NULL) |
| 188 | { |
| 189 | fprintf(stderr, "Failed to allocate block mapping memory.\n"); |
| 190 | return NULL; |
| 191 | } |
| 192 | memset(pMapping, 0xFF, numSrcBlocks * sizeof(unsigned short)); |
| 193 | |
| 194 | size_t total, erase, write; |
| 195 | if (mtd_partition_info(pReservoirPart, &total, &erase, &write) != 0) |
| 196 | { |
| 197 | fprintf(stderr, "Failed to access reservoir partition.\n"); |
| 198 | free(pMapping); |
| 199 | return NULL; |
| 200 | } |
| 201 | |
| 202 | if (erase != srcErase || write != srcWrite) |
| 203 | { |
| 204 | fprintf(stderr, "Source partition and reservoir partition differ in size properties.\n"); |
| 205 | free(pMapping); |
| 206 | return NULL; |
| 207 | } |
| 208 | |
| 209 | printf("Partition info: Total %d, Erase %d, write %d\n", total, erase, write); |
| 210 | |
| 211 | BmlOverMtdReadContext *readctx = bml_over_mtd_read_partition(pReservoirPart); |
| 212 | if (readctx == NULL) |
| 213 | { |
| 214 | fprintf(stderr, "Failed to open reservoir partition for reading.\n"); |
| 215 | free(pMapping); |
| 216 | return NULL; |
| 217 | } |
| 218 | |
| 219 | if (total < erase || total > INT_MAX) |
| 220 | { |
| 221 | fprintf(stderr, "Unsuitable reservoir partition properties.\n"); |
| 222 | free(pMapping); |
| 223 | bml_over_mtd_read_close(readctx); |
| 224 | return NULL; |
| 225 | } |
| 226 | |
| 227 | int foundMappingTable = 0; |
| 228 | |
| 229 | int currOffset = total; //Offset *behind* the last byte |
| 230 | while (currOffset > 0) |
| 231 | { |
| 232 | currOffset -= erase; |
| 233 | loff_t pos = lseek64(readctx->fd, currOffset, SEEK_SET); |
| 234 | int mgbb = ioctl(readctx->fd, MEMGETBADBLOCK, &pos); |
| 235 | if (mgbb != 0) |
| 236 | { |
| 237 | printf("Bad block %d in reservoir area, skipping.\n", currOffset/erase); |
| 238 | continue; |
| 239 | } |
| 240 | ssize_t readBytes = read(readctx->fd, readctx->buffer, erase); |
| 241 | if (readBytes != (ssize_t)erase) |
| 242 | { |
| 243 | fprintf(stderr, "Failed to read good block in reservoir area (%s).\n", |
| 244 | strerror(errno)); |
| 245 | free(pMapping); |
| 246 | bml_over_mtd_read_close(readctx); |
| 247 | return NULL; |
| 248 | } |
| 249 | if (readBytes >= 0x2000) |
| 250 | { |
| 251 | char* buf = readctx->buffer; |
| 252 | if (buf[0]=='U' && buf[1]=='P' && buf[2]=='C' && buf[3]=='H') |
| 253 | { |
| 254 | printf ("Found mapping block mark at 0x%x (block %d).\n", currOffset, currOffset/erase); |
| 255 | |
| 256 | unsigned short* mappings = (unsigned short*) &buf[0x1000]; |
| 257 | if (mappings[0]==0 && mappings[1]==0xffff) |
| 258 | { |
| 259 | printf("Found start of mapping table.\n"); |
| 260 | foundMappingTable = 1; |
| 261 | //Skip first entry (dummy) |
| 262 | unsigned short* mappingEntry = mappings + 2; |
| 263 | while (mappingEntry - mappings < 100 |
| 264 | && mappingEntry[0] != 0xffff) |
| 265 | { |
| 266 | unsigned short rawSrcBlk = mappingEntry[0]; |
| 267 | unsigned short rawDstBlk = mappingEntry[1]; |
| 268 | |
| 269 | printf("Found raw block mapping %d -> %d\n", rawSrcBlk, |
| 270 | rawDstBlk); |
| 271 | |
| 272 | unsigned int srcAbsoluteStartAddress = srcPartStartBlock * erase; |
| 273 | unsigned int resAbsoluteStartAddress = reservoirPartStartBlock * erase; |
| 274 | |
| 275 | int reservoirLastBlock = reservoirPartStartBlock + numSrcBlocks - 1; |
| 276 | if (rawDstBlk < reservoirPartStartBlock |
| 277 | || rawDstBlk*erase >= resAbsoluteStartAddress+currOffset) |
| 278 | { |
| 279 | fprintf(stderr, "Mapped block not within reasonable reservoir area.\n"); |
| 280 | foundMappingTable = 0; |
| 281 | break; |
| 282 | } |
| 283 | |
| 284 | int srcLastBlock = srcPartStartBlock + numSrcBlocks - 1; |
| 285 | if (rawSrcBlk >= srcPartStartBlock && rawSrcBlk <= srcLastBlock) |
| 286 | { |
| 287 | |
| 288 | unsigned short relSrcBlk = rawSrcBlk - srcPartStartBlock; |
| 289 | unsigned short relDstBlk = rawDstBlk - reservoirPartStartBlock; |
| 290 | printf("Partition relative block mapping %d -> %d\n",relSrcBlk, relDstBlk); |
| 291 | |
| 292 | printf("Absolute mapped start addresses 0x%x -> 0x%x\n", |
| 293 | srcAbsoluteStartAddress+relSrcBlk*erase, |
| 294 | resAbsoluteStartAddress+relDstBlk*erase); |
| 295 | printf("Partition relative mapped start addresses 0x%x -> 0x%x\n", |
| 296 | relSrcBlk*erase, relDstBlk*erase); |
| 297 | |
| 298 | //Set mapping entry. For duplicate entries, later entries replace former ones. |
| 299 | //*Assumption*: Bad blocks in reservoir area will not be mapped themselves in |
| 300 | //the mapping table. User partition blocks will not be mapped to bad blocks |
| 301 | //(only) in the reservoir area. This has to be confirmed on a wider range of |
| 302 | //devices. |
| 303 | pMapping[relSrcBlk] = relDstBlk; |
| 304 | |
| 305 | } |
| 306 | mappingEntry+=2; |
| 307 | } |
| 308 | break; //We found the mapping table, no need to search further |
| 309 | } |
| 310 | |
| 311 | |
| 312 | } |
| 313 | } |
| 314 | |
| 315 | } |
| 316 | bml_over_mtd_read_close(readctx); |
| 317 | |
| 318 | if (foundMappingTable == 0) |
| 319 | { |
| 320 | fprintf(stderr, "Cannot find mapping table in reservoir partition.\n"); |
| 321 | free(pMapping); |
| 322 | return NULL; |
| 323 | } |
| 324 | |
| 325 | //Consistency and validity check |
| 326 | int mappingValid = 1; |
| 327 | readctx = bml_over_mtd_read_partition(pSrcPart); |
| 328 | if (readctx == NULL) |
| 329 | { |
| 330 | fprintf(stderr, "Cannot open source partition for reading.\n"); |
| 331 | free(pMapping); |
| 332 | return NULL; |
| 333 | } |
| 334 | int currBlock = 0; |
| 335 | for (;currBlock < numSrcBlocks; ++currBlock) |
| 336 | { |
| 337 | loff_t pos = lseek64(readctx->fd, currBlock*erase, SEEK_SET); |
| 338 | int mgbb = ioctl(readctx->fd, MEMGETBADBLOCK, &pos); |
| 339 | if (mgbb == 0) |
| 340 | { |
| 341 | if (pMapping[currBlock]!=0xffff) |
| 342 | { |
| 343 | fprintf(stderr, "Consistency error: Good block has mapping entry %d -> %d\n", currBlock, pMapping[currBlock]); |
| 344 | mappingValid = 0; |
| 345 | } |
| 346 | } else |
| 347 | { |
| 348 | //Bad block! |
| 349 | if (pMapping[currBlock]==0xffff) |
| 350 | { |
| 351 | fprintf(stderr, "Consistency error: Bad block has no mapping entry \n"); |
| 352 | mappingValid = 0; |
| 353 | } else |
| 354 | { |
| 355 | BmlOverMtdReadContext* reservoirReadCtx = bml_over_mtd_read_partition(pReservoirPart); |
| 356 | if (reservoirReadCtx == 0) |
| 357 | { |
| 358 | fprintf(stderr, "Reservoir partition cannot be opened for reading in consistency check.\n"); |
| 359 | mappingValid = 0; |
| 360 | } else |
| 361 | { |
| 362 | pos = lseek64(reservoirReadCtx->fd, pMapping[currBlock]*erase, SEEK_SET); |
| 363 | mgbb = ioctl(reservoirReadCtx->fd, MEMGETBADBLOCK, &pos); |
| 364 | if (mgbb == 0) |
| 365 | { |
| 366 | printf("Bad block has properly mapped reservoir block %d -> %d\n",currBlock, pMapping[currBlock]); |
| 367 | } |
| 368 | else |
| 369 | { |
| 370 | fprintf(stderr, "Consistency error: Mapped block is bad, too. (%d -> %d)\n",currBlock, pMapping[currBlock]); |
| 371 | mappingValid = 0; |
| 372 | } |
| 373 | |
| 374 | } |
| 375 | bml_over_mtd_read_close(reservoirReadCtx); |
| 376 | } |
| 377 | |
| 378 | } |
| 379 | |
| 380 | } |
| 381 | bml_over_mtd_read_close(readctx); |
| 382 | |
| 383 | |
| 384 | if (!mappingValid) |
| 385 | { |
| 386 | free(pMapping); |
| 387 | return NULL; |
| 388 | } |
| 389 | |
| 390 | return pMapping; |
| 391 | } |
| 392 | |
| 393 | static void ReleaseBlockMapping(const unsigned short* blockMapping) |
| 394 | { |
| 395 | free((void*)blockMapping); |
| 396 | } |
| 397 | |
| 398 | static int dump_bml_partition(const MtdPartition* pSrcPart, const MtdPartition* pReservoirPart, |
| 399 | const unsigned short* blockMapping, const char* filename) |
| 400 | { |
| 401 | int fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666); |
| 402 | if (fd < 0) |
| 403 | { |
| 404 | fprintf(stderr, "error opening %s", filename); |
| 405 | return -1; |
| 406 | } |
| 407 | BmlOverMtdReadContext* pSrcRead = bml_over_mtd_read_partition(pSrcPart); |
| 408 | if (pSrcRead == NULL) |
| 409 | { |
| 410 | close(fd); |
| 411 | fprintf(stderr, "dump_bml_partition: Error opening src part for reading.\n"); |
| 412 | return -1; |
| 413 | } |
| 414 | |
| 415 | BmlOverMtdReadContext* pResRead = bml_over_mtd_read_partition(pReservoirPart); |
| 416 | if (pResRead == NULL) |
| 417 | { |
| 418 | close(fd); |
| 419 | bml_over_mtd_read_close(pSrcRead); |
| 420 | fprintf(stderr, "dump_bml_partition: Error opening reservoir part for reading.\n"); |
| 421 | return -1; |
| 422 | } |
| 423 | |
| 424 | |
| 425 | int numBlocks = pSrcPart->size / pSrcPart->erase_size; |
| 426 | int currblock = 0; |
| 427 | for (;currblock < numBlocks; ++currblock) |
| 428 | { |
| 429 | int srcFd = -1; |
| 430 | if (blockMapping[currblock] == 0xffff) |
| 431 | { |
| 432 | //Good block, use src partition |
| 433 | srcFd = pSrcRead->fd; |
| 434 | if (lseek64(pSrcRead->fd, currblock*pSrcPart->erase_size, SEEK_SET)==-1) |
| 435 | { |
| 436 | close(fd); |
| 437 | bml_over_mtd_read_close(pSrcRead); |
| 438 | bml_over_mtd_read_close(pResRead); |
| 439 | fprintf(stderr, "dump_bml_partition: lseek in src partition failed\n"); |
| 440 | return -1; |
| 441 | } |
| 442 | } else |
| 443 | { |
| 444 | //Bad block, use mapped block in reservoir partition |
| 445 | srcFd = pResRead->fd; |
| 446 | if (lseek64(pResRead->fd, blockMapping[currblock]*pSrcPart->erase_size, SEEK_SET)==-1) |
| 447 | { |
| 448 | close(fd); |
| 449 | bml_over_mtd_read_close(pSrcRead); |
| 450 | bml_over_mtd_read_close(pResRead); |
| 451 | fprintf(stderr, "dump_bml_partition: lseek in reservoir partition failed\n"); |
| 452 | return -1; |
| 453 | } |
| 454 | } |
| 455 | size_t blockBytesRead = 0; |
| 456 | while (blockBytesRead < pSrcPart->erase_size) |
| 457 | { |
| 458 | ssize_t len = read(srcFd, pSrcRead->buffer + blockBytesRead, |
| 459 | pSrcPart->erase_size - blockBytesRead); |
| 460 | if (len <= 0) |
| 461 | { |
| 462 | close(fd); |
| 463 | bml_over_mtd_read_close(pSrcRead); |
| 464 | bml_over_mtd_read_close(pResRead); |
| 465 | fprintf(stderr, "dump_bml_partition: reading partition failed\n"); |
| 466 | return -1; |
| 467 | } |
| 468 | blockBytesRead += len; |
| 469 | } |
| 470 | |
| 471 | size_t blockBytesWritten = 0; |
| 472 | while (blockBytesWritten < pSrcPart->erase_size) |
| 473 | { |
| 474 | ssize_t len = write(fd, pSrcRead->buffer + blockBytesWritten, |
| 475 | pSrcPart->erase_size - blockBytesWritten); |
| 476 | if (len <= 0) |
| 477 | { |
| 478 | close(fd); |
| 479 | bml_over_mtd_read_close(pSrcRead); |
| 480 | bml_over_mtd_read_close(pResRead); |
| 481 | fprintf(stderr, "dump_bml_partition: writing partition dump file failed\n"); |
| 482 | return -1; |
| 483 | } |
| 484 | blockBytesWritten += len; |
| 485 | } |
| 486 | |
| 487 | } |
| 488 | |
| 489 | bml_over_mtd_read_close(pSrcRead); |
| 490 | bml_over_mtd_read_close(pResRead); |
| 491 | |
| 492 | if (close(fd)) { |
| 493 | unlink(filename); |
| 494 | printf("error closing %s", filename); |
| 495 | return -1; |
| 496 | } |
| 497 | |
| 498 | return 0; |
| 499 | } |
| 500 | |
| 501 | static ssize_t bml_over_mtd_write_block(int fd, ssize_t erase_size, char* data) |
| 502 | { |
| 503 | off_t pos = lseek(fd, 0, SEEK_CUR); |
| 504 | if (pos == (off_t) -1) return -1; |
| 505 | |
| 506 | ssize_t size = erase_size; |
| 507 | loff_t bpos = pos; |
| 508 | int ret = ioctl(fd, MEMGETBADBLOCK, &bpos); |
| 509 | if (ret != 0 && !(ret == -1 && errno == EOPNOTSUPP)) { |
| 510 | fprintf(stderr, |
| 511 | "Mapping failure: Trying to write bad block at 0x%08lx (ret %d errno %d)\n", |
| 512 | pos, ret, errno); |
| 513 | return -1; |
| 514 | } |
| 515 | |
| 516 | struct erase_info_user erase_info; |
| 517 | erase_info.start = pos; |
| 518 | erase_info.length = size; |
| 519 | int retry; |
| 520 | for (retry = 0; retry < 2; ++retry) { |
| 521 | if (ioctl(fd, MEMERASE, &erase_info) < 0) { |
| 522 | fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n", |
| 523 | pos, strerror(errno)); |
| 524 | continue; |
| 525 | } |
| 526 | if (lseek(fd, pos, SEEK_SET) != pos || |
| 527 | write(fd, data, size) != size) { |
| 528 | fprintf(stderr, "mtd: write error at 0x%08lx (%s)\n", |
| 529 | pos, strerror(errno)); |
| 530 | } |
| 531 | |
| 532 | char verify[size]; |
| 533 | if (lseek(fd, pos, SEEK_SET) != pos || |
| 534 | read(fd, verify, size) != size) { |
| 535 | fprintf(stderr, "mtd: re-read error at 0x%08lx (%s)\n", |
| 536 | pos, strerror(errno)); |
| 537 | continue; |
| 538 | } |
| 539 | if (memcmp(data, verify, size) != 0) { |
| 540 | fprintf(stderr, "mtd: verification error at 0x%08lx (%s)\n", |
| 541 | pos, strerror(errno)); |
| 542 | continue; |
| 543 | } |
| 544 | |
| 545 | if (retry > 0) { |
| 546 | fprintf(stderr, "mtd: wrote block after %d retries\n", retry); |
| 547 | } |
| 548 | fprintf(stderr, "mtd: successfully wrote block at %llx\n", pos); |
| 549 | return size; // Success! |
| 550 | } |
| 551 | |
| 552 | |
| 553 | fprintf(stderr, "mtd: Block at %llx could not be properly written.\n", pos); |
| 554 | // Ran out of space on the device |
| 555 | errno = ENOSPC; |
| 556 | return -1; |
| 557 | } |
| 558 | |
| 559 | static int flash_bml_partition(const MtdPartition* pSrcPart, const MtdPartition* pReservoirPart, |
| 560 | const unsigned short* blockMapping, const char* filename) |
| 561 | { |
| 562 | int fd = open(filename, O_RDONLY); |
| 563 | if (fd < 0) |
| 564 | { |
| 565 | fprintf(stderr, "error opening %s", filename); |
| 566 | return -1; |
| 567 | } |
| 568 | BmlOverMtdWriteContext* pSrcWrite = bml_over_mtd_write_partition(pSrcPart); |
| 569 | if (pSrcWrite == NULL) |
| 570 | { |
| 571 | close(fd); |
| 572 | fprintf(stderr, "flash_bml_partition: Error opening src part for writing.\n"); |
| 573 | return -1; |
| 574 | } |
| 575 | |
| 576 | #ifdef DUMMY_WRITING |
| 577 | close(pSrcWrite->fd); |
| 578 | pSrcWrite->fd = open("/sdcard/srcPartWriteDummy.bin", O_WRONLY|O_CREAT|O_TRUNC, 0666); |
| 579 | #endif |
| 580 | |
| 581 | BmlOverMtdWriteContext* pResWrite = bml_over_mtd_write_partition(pReservoirPart); |
| 582 | if (pResWrite == NULL) |
| 583 | { |
| 584 | close(fd); |
| 585 | bml_over_mtd_write_close(pSrcWrite); |
| 586 | fprintf(stderr, "flash_bml_partition: Error opening reservoir part for writing.\n"); |
| 587 | return -1; |
| 588 | } |
| 589 | #ifdef DUMMY_WRITING |
| 590 | close(pResWrite->fd); |
| 591 | pResWrite->fd = open("/sdcard/resPartWriteDummy.bin", O_WRONLY|O_CREAT|O_TRUNC, 0666); |
| 592 | #endif |
| 593 | |
| 594 | struct stat fileStat; |
| 595 | if (fstat(fd, &fileStat) != 0) |
| 596 | { |
| 597 | close(fd); |
| 598 | bml_over_mtd_write_close(pSrcWrite); |
| 599 | bml_over_mtd_write_close(pResWrite); |
| 600 | fprintf(stderr, "flash_bml_partition: Failed to stat source file.\n"); |
| 601 | return -1; |
| 602 | |
| 603 | } |
| 604 | if (fileStat.st_size > pSrcPart->size) |
| 605 | { |
| 606 | close(fd); |
| 607 | bml_over_mtd_write_close(pSrcWrite); |
| 608 | bml_over_mtd_write_close(pResWrite); |
| 609 | fprintf(stderr, "flash_bml_partition: Source file too large for target partition.\n"); |
| 610 | return -1; |
| 611 | } |
| 612 | |
| 613 | int numBlocks = (fileStat.st_size + pSrcPart->erase_size - 1) / pSrcPart->erase_size; |
| 614 | int currblock; |
| 615 | for (currblock = 0 ;currblock < numBlocks; ++currblock) |
| 616 | { |
| 617 | memset(pSrcWrite->buffer, 0xFF, pSrcPart->erase_size); |
| 618 | size_t blockBytesRead = 0; |
| 619 | while (blockBytesRead < pSrcPart->erase_size) |
| 620 | { |
| 621 | ssize_t len = read(fd, pSrcWrite->buffer + blockBytesRead, |
| 622 | pSrcPart->erase_size - blockBytesRead); |
| 623 | if (len < 0) |
| 624 | { |
| 625 | close(fd); |
| 626 | bml_over_mtd_write_close(pSrcWrite); |
| 627 | bml_over_mtd_write_close(pResWrite); |
| 628 | fprintf(stderr, "flash_bml_partition: read source file failed\n"); |
| 629 | return -1; |
| 630 | } |
| 631 | if (len == 0) |
| 632 | { |
| 633 | //End of file |
| 634 | break; |
| 635 | } |
| 636 | |
| 637 | blockBytesRead += len; |
| 638 | } |
| 639 | |
| 640 | |
| 641 | |
| 642 | int srcFd = -1; |
| 643 | if (blockMapping[currblock] == 0xffff) |
| 644 | { |
| 645 | //Good block, use src partition |
| 646 | srcFd = pSrcWrite->fd; |
| 647 | if (lseek64(pSrcWrite->fd, currblock*pSrcPart->erase_size, SEEK_SET)==-1) |
| 648 | { |
| 649 | close(fd); |
| 650 | bml_over_mtd_write_close(pSrcWrite); |
| 651 | bml_over_mtd_write_close(pResWrite); |
| 652 | fprintf(stderr, "flash_bml_partition: lseek in src partition failed\n"); |
| 653 | return -1; |
| 654 | } |
| 655 | } else |
| 656 | { |
| 657 | //Bad block, use mapped block in reservoir partition |
| 658 | srcFd = pResWrite->fd; |
| 659 | if (lseek64(pResWrite->fd, blockMapping[currblock]*pSrcPart->erase_size, SEEK_SET)==-1) |
| 660 | { |
| 661 | close(fd); |
| 662 | bml_over_mtd_write_close(pSrcWrite); |
| 663 | bml_over_mtd_write_close(pResWrite); |
| 664 | fprintf(stderr, "flash_bml_partition: lseek in reservoir partition failed\n"); |
| 665 | return -1; |
| 666 | } |
| 667 | } |
| 668 | size_t blockBytesWritten = 0; |
| 669 | while (blockBytesWritten < pSrcPart->erase_size) |
| 670 | { |
| 671 | #ifdef DUMMY_WRITING |
| 672 | ssize_t len = write(srcFd, pSrcWrite->buffer + blockBytesWritten, |
| 673 | pSrcPart->erase_size - blockBytesWritten); |
| 674 | #else |
| 675 | ssize_t len = bml_over_mtd_write_block(srcFd, pSrcPart->erase_size, pSrcWrite->buffer); |
| 676 | #endif |
| 677 | if (len <= 0) |
| 678 | { |
| 679 | close(fd); |
| 680 | bml_over_mtd_write_close(pSrcWrite); |
| 681 | bml_over_mtd_write_close(pResWrite); |
| 682 | fprintf(stderr, "flash_bml_partition: writing to partition failed\n"); |
| 683 | return -1; |
| 684 | } |
| 685 | blockBytesWritten += len; |
| 686 | } |
| 687 | |
| 688 | |
| 689 | } |
| 690 | |
| 691 | bml_over_mtd_write_close(pSrcWrite); |
| 692 | bml_over_mtd_write_close(pResWrite); |
| 693 | |
| 694 | if (close(fd)) { |
| 695 | printf("error closing %s", filename); |
| 696 | return -1; |
| 697 | } |
| 698 | |
| 699 | return 0; |
| 700 | } |
| 701 | |
| 702 | static int scan_partition(const MtdPartition* pPart) |
| 703 | { |
| 704 | BmlOverMtdReadContext* readCtx = bml_over_mtd_read_partition(pPart); |
| 705 | if (readCtx == NULL) |
| 706 | { |
| 707 | fprintf(stderr, "Failed to open partition for reading.\n"); |
| 708 | return -1; |
| 709 | } |
| 710 | |
| 711 | int numBadBlocks = 0; |
| 712 | size_t numBlocks = pPart->size / pPart->erase_size; |
| 713 | size_t currBlock; |
| 714 | for (currBlock = 0; currBlock < numBlocks; ++currBlock) |
| 715 | { |
| 716 | |
| 717 | loff_t pos = currBlock * pPart->erase_size; |
| 718 | int mgbb = ioctl(readCtx->fd, MEMGETBADBLOCK, &pos); |
| 719 | if (mgbb != 0) |
| 720 | { |
| 721 | printf("Bad block %d at 0x%x.\n", currBlock, (unsigned int)pos); |
| 722 | numBadBlocks++; |
| 723 | } |
| 724 | } |
| 725 | |
| 726 | bml_over_mtd_read_close(readCtx); |
| 727 | if (numBadBlocks == 0) |
| 728 | { |
| 729 | printf("No bad blocks.\n"); |
| 730 | return 0; |
| 731 | } |
| 732 | return -1 ; |
| 733 | } |
| 734 | |
| 735 | int main(int argc, char **argv) |
| 736 | { |
| 737 | if (argc != 7 && (argc != 3 || (argc == 3 && strcmp(argv[1],"scan"))!=0) |
| 738 | && (argc != 6 || (argc == 6 && strcmp(argv[1],"scan"))!=0)) |
| 739 | return die("Usage: %s dump|flash <partition> <partition_start_block> <reservoirpartition> <reservoir_start_block> <file>\n" |
| 740 | "E.g. %s dump boot 72 reservoir 2004 file.bin\n" |
| 741 | "Usage: %s scan <partition> [<partition_start_block> <reservoirpartition> <reservoir_start_block>]\n" |
| 742 | ,argv[0], argv[0], argv[0]); |
| 743 | int num_partitions = mtd_scan_partitions(); |
| 744 | const MtdPartition *pSrcPart = mtd_find_partition_by_name(argv[2]); |
| 745 | if (pSrcPart == NULL) |
| 746 | return die("Cannot find partition %s", argv[2]); |
| 747 | |
| 748 | int scanResult = scan_partition(pSrcPart); |
| 749 | |
| 750 | if (argc == 3 && strcmp(argv[1],"scan")==0) |
| 751 | { |
| 752 | return (scanResult == 0 ? 0 : EXIT_CODE_BAD_BLOCKS); |
| 753 | } |
| 754 | |
| 755 | int retVal = 0; |
| 756 | const MtdPartition* pReservoirPart = mtd_find_partition_by_name(argv[4]); |
| 757 | if (pReservoirPart == NULL) |
| 758 | return die("Cannot find partition %s", argv[4]); |
| 759 | |
| 760 | int srcPartStartBlock = atoi(argv[3]); |
| 761 | int reservoirPartStartBlock = atoi(argv[5]); |
| 762 | const unsigned short* pMapping = CreateBlockMapping(pSrcPart, srcPartStartBlock, |
| 763 | pReservoirPart, reservoirPartStartBlock); |
| 764 | |
| 765 | if (pMapping == NULL && scanResult == 0) |
| 766 | { |
| 767 | printf("Generating empty block mapping table for error-free partition.\n"); |
| 768 | pMapping = CreateEmptyBlockMapping(pSrcPart); |
| 769 | } |
| 770 | |
| 771 | if (argc == 6 && strcmp(argv[1],"scan")==0) |
| 772 | { |
| 773 | retVal = (scanResult == 0 ? 0 : EXIT_CODE_BAD_BLOCKS); |
| 774 | } |
| 775 | |
| 776 | if (pMapping == NULL) |
| 777 | return die("Failed to create block mapping table"); |
| 778 | |
| 779 | if (strcmp(argv[1],"dump")==0) |
| 780 | { |
| 781 | retVal = dump_bml_partition(pSrcPart, pReservoirPart, pMapping, argv[6]); |
| 782 | if (retVal == 0) |
| 783 | printf("Successfully dumped partition to %s\n", argv[6]); |
| 784 | } |
| 785 | |
| 786 | if (strcmp(argv[1],"flash")==0) |
| 787 | { |
| 788 | retVal = flash_bml_partition(pSrcPart, pReservoirPart, pMapping, argv[6]); |
| 789 | if (retVal == 0) |
| 790 | printf("Successfully wrote %s to partition\n", argv[6]); |
| 791 | |
| 792 | } |
| 793 | |
| 794 | |
| 795 | ReleaseBlockMapping(pMapping); |
| 796 | return retVal; |
| 797 | } |
| 798 | |