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