diff options
Diffstat (limited to 'mtdutils')
-rw-r--r-- | mtdutils/Android.mk | 51 | ||||
-rw-r--r-- | mtdutils/bml_over_mtd.c | 810 | ||||
-rw-r--r-- | mtdutils/mounts.c | 90 | ||||
-rw-r--r-- | mtdutils/mounts.h | 8 | ||||
-rw-r--r-- | mtdutils/mtdutils.c | 257 | ||||
-rw-r--r-- | mtdutils/mtdutils.h | 13 | ||||
-rw-r--r-- | mtdutils/rk3xhack.c | 60 | ||||
-rw-r--r-- | mtdutils/rk3xhack.h | 37 |
8 files changed, 1282 insertions, 44 deletions
diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk index b7d35c27a..7e5fadc71 100644 --- a/mtdutils/Android.mk +++ b/mtdutils/Android.mk @@ -1,20 +1,61 @@ +ifneq ($(TARGET_SIMULATOR),true) + LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ mtdutils.c \ - mounts.c + mounts.c + +ifneq ($(filter rk30xx rk3188,$(TARGET_BOARD_PLATFORM)),) +LOCAL_SRC_FILES += rk3xhack.c +LOCAL_CFLAGS += -DRK3X +endif LOCAL_MODULE := libmtdutils +LOCAL_STATIC_LIBRARIES := libcutils libc LOCAL_CLANG := true include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + mtdutils.c \ + mounts.c + +ifneq ($(filter rk30xx rk3188,$(TARGET_BOARD_PLATFORM)),) +LOCAL_SRC_FILES += rk3xhack.c +LOCAL_CFLAGS += -DRK3X +endif + +LOCAL_MODULE := libmtdutils +LOCAL_SHARED_LIBRARIES := libcutils libc LOCAL_CLANG := true -LOCAL_SRC_FILES := flash_image.c -LOCAL_MODULE := flash_image + +include $(BUILD_SHARED_LIBRARY) + +ifeq ($(BOARD_USES_BML_OVER_MTD),true) +include $(CLEAR_VARS) +LOCAL_SRC_FILES := bml_over_mtd.c +LOCAL_C_INCLUDES += $(commands_recovery_local_path)/mtdutils +LOCAL_MODULE := libbml_over_mtd +LOCAL_MODULE_TAGS := eng +LOCAL_CFLAGS += -Dmain=bml_over_mtd_main +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := bml_over_mtd.c +LOCAL_MODULE := bml_over_mtd LOCAL_MODULE_TAGS := eng -LOCAL_STATIC_LIBRARIES := libmtdutils -LOCAL_SHARED_LIBRARIES := libcutils liblog libc +LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES +LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities +LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities +LOCAL_MODULE_STEM := bml_over_mtd +LOCAL_C_INCLUDES += $(commands_recovery_local_path)/mtdutils +LOCAL_SHARED_LIBRARIES := libmtdutils libcutils liblog libc include $(BUILD_EXECUTABLE) +endif + +endif # !TARGET_SIMULATOR + diff --git a/mtdutils/bml_over_mtd.c b/mtdutils/bml_over_mtd.c new file mode 100644 index 000000000..12ca10915 --- /dev/null +++ b/mtdutils/bml_over_mtd.c @@ -0,0 +1,810 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <limits.h> + +#include "cutils/log.h" + +#include <mtd/mtd-user.h> + +#include "mtdutils.h" + +#ifdef RK3X + #include "rk3xhack.h" +#endif + +typedef struct BmlOverMtdReadContext { + const MtdPartition *partition; + char *buffer; + size_t consumed; + int fd; +} BmlOverMtdReadContext; + +typedef struct BmlOverMtdWriteContext { + const MtdPartition *partition; + char *buffer; + size_t stored; + int fd; + + off_t* bad_block_offsets; + int bad_block_alloc; + int bad_block_count; +} BmlOverMtdWriteContext; + + +static BmlOverMtdReadContext *bml_over_mtd_read_partition(const MtdPartition *partition) +{ + BmlOverMtdReadContext *ctx = (BmlOverMtdReadContext*) malloc(sizeof(BmlOverMtdReadContext)); + if (ctx == NULL) return NULL; + + ctx->buffer = malloc(partition->erase_size); + if (ctx->buffer == NULL) { + free(ctx); + return NULL; + } + + char mtddevname[32]; + sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index); + ctx->fd = open(mtddevname, O_RDONLY); + if (ctx->fd < 0) { + free(ctx); + free(ctx->buffer); + return NULL; + } + + ctx->partition = partition; + ctx->consumed = partition->erase_size; + return ctx; +} + +static void bml_over_mtd_read_close(BmlOverMtdReadContext *ctx) +{ + close(ctx->fd); + free(ctx->buffer); + free(ctx); +} + +static BmlOverMtdWriteContext *bml_over_mtd_write_partition(const MtdPartition *partition) +{ + BmlOverMtdWriteContext *ctx = (BmlOverMtdWriteContext*) malloc(sizeof(BmlOverMtdWriteContext)); + if (ctx == NULL) return NULL; + + ctx->bad_block_offsets = NULL; + ctx->bad_block_alloc = 0; + ctx->bad_block_count = 0; + + ctx->buffer = malloc(partition->erase_size); + if (ctx->buffer == NULL) { + free(ctx); + return NULL; + } + + char mtddevname[32]; + sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index); + ctx->fd = open(mtddevname, O_RDWR); + if (ctx->fd < 0) { + free(ctx->buffer); + free(ctx); + return NULL; + } + + ctx->partition = partition; + ctx->stored = 0; + return ctx; +} + +static int bml_over_mtd_write_close(BmlOverMtdWriteContext *ctx) +{ + int r = 0; + if (close(ctx->fd)) r = -1; + free(ctx->bad_block_offsets); + free(ctx->buffer); + free(ctx); + return r; +} + + +#ifdef LOG_TAG +#undef LOG_TAG +#endif + +#define LOG_TAG "bml_over_mtd" + +#define BLOCK_SIZE 2048 +#define SPARE_SIZE (BLOCK_SIZE >> 5) + +#define EXIT_CODE_BAD_BLOCKS 15 + +static int die(const char *msg, ...) { + int err = errno; + va_list args; + va_start(args, msg); + char buf[1024]; + vsnprintf(buf, sizeof(buf), msg, args); + va_end(args); + + if (err != 0) { + strlcat(buf, ": ", sizeof(buf)); + strlcat(buf, strerror(err), sizeof(buf)); + } + + fprintf(stderr, "%s\n", buf); + return 1; +} + +static unsigned short* CreateEmptyBlockMapping(const MtdPartition* pSrcPart) +{ + size_t srcTotal, srcErase, srcWrite; + if (mtd_partition_info(pSrcPart, &srcTotal, &srcErase, &srcWrite) != 0) + { + fprintf(stderr, "Failed to access partition.\n"); + return NULL; + } + + int numSrcBlocks = srcTotal/srcErase; + + unsigned short* pMapping = malloc(numSrcBlocks * sizeof(unsigned short)); + if (pMapping == NULL) + { + fprintf(stderr, "Failed to allocate block mapping memory.\n"); + return NULL; + } + memset(pMapping, 0xFF, numSrcBlocks * sizeof(unsigned short)); + return pMapping; +} + +static const unsigned short* CreateBlockMapping(const MtdPartition* pSrcPart, int srcPartStartBlock, + const MtdPartition *pReservoirPart, int reservoirPartStartBlock) +{ + size_t srcTotal, srcErase, srcWrite; + if (mtd_partition_info(pSrcPart, &srcTotal, &srcErase, &srcWrite) != 0) + { + fprintf(stderr, "Failed to access partition.\n"); + return NULL; + } + + int numSrcBlocks = srcTotal/srcErase; + + unsigned short* pMapping = malloc(numSrcBlocks * sizeof(unsigned short)); + if (pMapping == NULL) + { + fprintf(stderr, "Failed to allocate block mapping memory.\n"); + return NULL; + } + memset(pMapping, 0xFF, numSrcBlocks * sizeof(unsigned short)); + + size_t total, erase, write; + if (mtd_partition_info(pReservoirPart, &total, &erase, &write) != 0) + { + fprintf(stderr, "Failed to access reservoir partition.\n"); + free(pMapping); + return NULL; + } + + if (erase != srcErase || write != srcWrite) + { + fprintf(stderr, "Source partition and reservoir partition differ in size properties.\n"); + free(pMapping); + return NULL; + } + + printf("Partition info: Total %d, Erase %d, write %d\n", total, erase, write); + + BmlOverMtdReadContext *readctx = bml_over_mtd_read_partition(pReservoirPart); + if (readctx == NULL) + { + fprintf(stderr, "Failed to open reservoir partition for reading.\n"); + free(pMapping); + return NULL; + } + + if (total < erase || total > INT_MAX) + { + fprintf(stderr, "Unsuitable reservoir partition properties.\n"); + free(pMapping); + bml_over_mtd_read_close(readctx); + return NULL; + } + + int foundMappingTable = 0; + + int currOffset = total; //Offset *behind* the last byte + while (currOffset > 0) + { + currOffset -= erase; + loff_t pos = lseek64(readctx->fd, currOffset, SEEK_SET); + int mgbb = ioctl(readctx->fd, MEMGETBADBLOCK, &pos); + if (mgbb != 0) + { + printf("Bad block %d in reservoir area, skipping.\n", currOffset/erase); + continue; + } + ssize_t readBytes = read(readctx->fd, readctx->buffer, erase); + if (readBytes != (ssize_t)erase) + { + fprintf(stderr, "Failed to read good block in reservoir area (%s).\n", + strerror(errno)); + free(pMapping); + bml_over_mtd_read_close(readctx); + return NULL; + } + if (readBytes >= 0x2000) + { + char* buf = readctx->buffer; + if (buf[0]=='U' && buf[1]=='P' && buf[2]=='C' && buf[3]=='H') + { + printf ("Found mapping block mark at 0x%x (block %d).\n", currOffset, currOffset/erase); + + unsigned short* mappings = (unsigned short*) &buf[0x1000]; + if (mappings[0]==0 && mappings[1]==0xffff) + { + printf("Found start of mapping table.\n"); + foundMappingTable = 1; + //Skip first entry (dummy) + unsigned short* mappingEntry = mappings + 2; + while (mappingEntry - mappings < 100 + && mappingEntry[0] != 0xffff) + { + unsigned short rawSrcBlk = mappingEntry[0]; + unsigned short rawDstBlk = mappingEntry[1]; + + printf("Found raw block mapping %d -> %d\n", rawSrcBlk, + rawDstBlk); + + unsigned int srcAbsoluteStartAddress = srcPartStartBlock * erase; + unsigned int resAbsoluteStartAddress = reservoirPartStartBlock * erase; + + int reservoirLastBlock = reservoirPartStartBlock + numSrcBlocks - 1; + if (rawDstBlk < reservoirPartStartBlock + || rawDstBlk*erase >= resAbsoluteStartAddress+currOffset) + { + fprintf(stderr, "Mapped block not within reasonable reservoir area.\n"); + foundMappingTable = 0; + break; + } + + int srcLastBlock = srcPartStartBlock + numSrcBlocks - 1; + if (rawSrcBlk >= srcPartStartBlock && rawSrcBlk <= srcLastBlock) + { + + unsigned short relSrcBlk = rawSrcBlk - srcPartStartBlock; + unsigned short relDstBlk = rawDstBlk - reservoirPartStartBlock; + printf("Partition relative block mapping %d -> %d\n",relSrcBlk, relDstBlk); + + printf("Absolute mapped start addresses 0x%x -> 0x%x\n", + srcAbsoluteStartAddress+relSrcBlk*erase, + resAbsoluteStartAddress+relDstBlk*erase); + printf("Partition relative mapped start addresses 0x%x -> 0x%x\n", + relSrcBlk*erase, relDstBlk*erase); + + //Set mapping entry. For duplicate entries, later entries replace former ones. + //*Assumption*: Bad blocks in reservoir area will not be mapped themselves in + //the mapping table. User partition blocks will not be mapped to bad blocks + //(only) in the reservoir area. This has to be confirmed on a wider range of + //devices. + pMapping[relSrcBlk] = relDstBlk; + + } + mappingEntry+=2; + } + break; //We found the mapping table, no need to search further + } + + + } + } + + } + bml_over_mtd_read_close(readctx); + + if (foundMappingTable == 0) + { + fprintf(stderr, "Cannot find mapping table in reservoir partition.\n"); + free(pMapping); + return NULL; + } + + //Consistency and validity check + int mappingValid = 1; + readctx = bml_over_mtd_read_partition(pSrcPart); + if (readctx == NULL) + { + fprintf(stderr, "Cannot open source partition for reading.\n"); + free(pMapping); + return NULL; + } + int currBlock = 0; + for (;currBlock < numSrcBlocks; ++currBlock) + { + loff_t pos = lseek64(readctx->fd, currBlock*erase, SEEK_SET); + int mgbb = ioctl(readctx->fd, MEMGETBADBLOCK, &pos); + if (mgbb == 0) + { + if (pMapping[currBlock]!=0xffff) + { + fprintf(stderr, "Consistency error: Good block has mapping entry %d -> %d\n", currBlock, pMapping[currBlock]); + mappingValid = 0; + } + } else + { + //Bad block! + if (pMapping[currBlock]==0xffff) + { + fprintf(stderr, "Consistency error: Bad block has no mapping entry \n"); + mappingValid = 0; + } else + { + BmlOverMtdReadContext* reservoirReadCtx = bml_over_mtd_read_partition(pReservoirPart); + if (reservoirReadCtx == 0) + { + fprintf(stderr, "Reservoir partition cannot be opened for reading in consistency check.\n"); + mappingValid = 0; + } else + { + pos = lseek64(reservoirReadCtx->fd, pMapping[currBlock]*erase, SEEK_SET); + mgbb = ioctl(reservoirReadCtx->fd, MEMGETBADBLOCK, &pos); + if (mgbb == 0) + { + printf("Bad block has properly mapped reservoir block %d -> %d\n",currBlock, pMapping[currBlock]); + } + else + { + fprintf(stderr, "Consistency error: Mapped block is bad, too. (%d -> %d)\n",currBlock, pMapping[currBlock]); + mappingValid = 0; + } + + } + bml_over_mtd_read_close(reservoirReadCtx); + } + + } + + } + bml_over_mtd_read_close(readctx); + + + if (!mappingValid) + { + free(pMapping); + return NULL; + } + + return pMapping; +} + +static void ReleaseBlockMapping(const unsigned short* blockMapping) +{ + free((void*)blockMapping); +} + +static int dump_bml_partition(const MtdPartition* pSrcPart, const MtdPartition* pReservoirPart, + const unsigned short* blockMapping, const char* filename) +{ + int fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666); + if (fd < 0) + { + fprintf(stderr, "error opening %s", filename); + return -1; + } + BmlOverMtdReadContext* pSrcRead = bml_over_mtd_read_partition(pSrcPart); + if (pSrcRead == NULL) + { + close(fd); + fprintf(stderr, "dump_bml_partition: Error opening src part for reading.\n"); + return -1; + } + + BmlOverMtdReadContext* pResRead = bml_over_mtd_read_partition(pReservoirPart); + if (pResRead == NULL) + { + close(fd); + bml_over_mtd_read_close(pSrcRead); + fprintf(stderr, "dump_bml_partition: Error opening reservoir part for reading.\n"); + return -1; + } + + + int numBlocks = pSrcPart->size / pSrcPart->erase_size; + int currblock = 0; + for (;currblock < numBlocks; ++currblock) + { + int srcFd = -1; + if (blockMapping[currblock] == 0xffff) + { + //Good block, use src partition + srcFd = pSrcRead->fd; + if (lseek64(pSrcRead->fd, currblock*pSrcPart->erase_size, SEEK_SET)==-1) + { + close(fd); + bml_over_mtd_read_close(pSrcRead); + bml_over_mtd_read_close(pResRead); + fprintf(stderr, "dump_bml_partition: lseek in src partition failed\n"); + return -1; + } + } else + { + //Bad block, use mapped block in reservoir partition + srcFd = pResRead->fd; + if (lseek64(pResRead->fd, blockMapping[currblock]*pSrcPart->erase_size, SEEK_SET)==-1) + { + close(fd); + bml_over_mtd_read_close(pSrcRead); + bml_over_mtd_read_close(pResRead); + fprintf(stderr, "dump_bml_partition: lseek in reservoir partition failed\n"); + return -1; + } + } + size_t blockBytesRead = 0; + while (blockBytesRead < pSrcPart->erase_size) + { + ssize_t len = read(srcFd, pSrcRead->buffer + blockBytesRead, + pSrcPart->erase_size - blockBytesRead); + if (len <= 0) + { + close(fd); + bml_over_mtd_read_close(pSrcRead); + bml_over_mtd_read_close(pResRead); + fprintf(stderr, "dump_bml_partition: reading partition failed\n"); + return -1; + } + blockBytesRead += len; + } + + size_t blockBytesWritten = 0; + while (blockBytesWritten < pSrcPart->erase_size) + { + ssize_t len = write(fd, pSrcRead->buffer + blockBytesWritten, + pSrcPart->erase_size - blockBytesWritten); + if (len <= 0) + { + close(fd); + bml_over_mtd_read_close(pSrcRead); + bml_over_mtd_read_close(pResRead); + fprintf(stderr, "dump_bml_partition: writing partition dump file failed\n"); + return -1; + } + blockBytesWritten += len; + } + + } + + bml_over_mtd_read_close(pSrcRead); + bml_over_mtd_read_close(pResRead); + + if (close(fd)) { + unlink(filename); + printf("error closing %s", filename); + return -1; + } + + return 0; +} + +static ssize_t bml_over_mtd_write_block(int fd, ssize_t erase_size, char* data) +{ + off_t pos = lseek(fd, 0, SEEK_CUR); + if (pos == (off_t) -1) return -1; + + ssize_t size = erase_size; + loff_t bpos = pos; + int ret = ioctl(fd, MEMGETBADBLOCK, &bpos); + if (ret != 0 && !(ret == -1 && errno == EOPNOTSUPP)) { + fprintf(stderr, + "Mapping failure: Trying to write bad block at 0x%08lx (ret %d errno %d)\n", + pos, ret, errno); + return -1; + } + + struct erase_info_user erase_info; + erase_info.start = pos; + erase_info.length = size; + int retry; + for (retry = 0; retry < 2; ++retry) { +#ifdef RK3X + if (rk30_zero_out(fd, pos, size) < 0) { + fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n", + pos, strerror(errno)); + continue; + } +#else + if (ioctl(fd, MEMERASE, &erase_info) < 0) { + fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n", + pos, strerror(errno)); + continue; + } +#endif + if (lseek(fd, pos, SEEK_SET) != pos || + write(fd, data, size) != size) { + fprintf(stderr, "mtd: write error at 0x%08lx (%s)\n", + pos, strerror(errno)); + } + + char verify[size]; + if (lseek(fd, pos, SEEK_SET) != pos || + read(fd, verify, size) != size) { + fprintf(stderr, "mtd: re-read error at 0x%08lx (%s)\n", + pos, strerror(errno)); + continue; + } + if (memcmp(data, verify, size) != 0) { + fprintf(stderr, "mtd: verification error at 0x%08lx (%s)\n", + pos, strerror(errno)); + continue; + } + + if (retry > 0) { + fprintf(stderr, "mtd: wrote block after %d retries\n", retry); + } + fprintf(stderr, "mtd: successfully wrote block at %llx\n", pos); + return size; // Success! + } + + + fprintf(stderr, "mtd: Block at %llx could not be properly written.\n", pos); + // Ran out of space on the device + errno = ENOSPC; + return -1; +} + +static int flash_bml_partition(const MtdPartition* pSrcPart, const MtdPartition* pReservoirPart, + const unsigned short* blockMapping, const char* filename) +{ + int fd = open(filename, O_RDONLY); + if (fd < 0) + { + fprintf(stderr, "error opening %s", filename); + return -1; + } + BmlOverMtdWriteContext* pSrcWrite = bml_over_mtd_write_partition(pSrcPart); + if (pSrcWrite == NULL) + { + close(fd); + fprintf(stderr, "flash_bml_partition: Error opening src part for writing.\n"); + return -1; + } + +#ifdef DUMMY_WRITING + close(pSrcWrite->fd); + pSrcWrite->fd = open("/sdcard/srcPartWriteDummy.bin", O_WRONLY|O_CREAT|O_TRUNC, 0666); +#endif + + BmlOverMtdWriteContext* pResWrite = bml_over_mtd_write_partition(pReservoirPart); + if (pResWrite == NULL) + { + close(fd); + bml_over_mtd_write_close(pSrcWrite); + fprintf(stderr, "flash_bml_partition: Error opening reservoir part for writing.\n"); + return -1; + } +#ifdef DUMMY_WRITING + close(pResWrite->fd); + pResWrite->fd = open("/sdcard/resPartWriteDummy.bin", O_WRONLY|O_CREAT|O_TRUNC, 0666); +#endif + + struct stat fileStat; + if (fstat(fd, &fileStat) != 0) + { + close(fd); + bml_over_mtd_write_close(pSrcWrite); + bml_over_mtd_write_close(pResWrite); + fprintf(stderr, "flash_bml_partition: Failed to stat source file.\n"); + return -1; + + } + if (fileStat.st_size > pSrcPart->size) + { + close(fd); + bml_over_mtd_write_close(pSrcWrite); + bml_over_mtd_write_close(pResWrite); + fprintf(stderr, "flash_bml_partition: Source file too large for target partition.\n"); + return -1; + } + + int numBlocks = (fileStat.st_size + pSrcPart->erase_size - 1) / pSrcPart->erase_size; + int currblock; + for (currblock = 0 ;currblock < numBlocks; ++currblock) + { + memset(pSrcWrite->buffer, 0xFF, pSrcPart->erase_size); + size_t blockBytesRead = 0; + while (blockBytesRead < pSrcPart->erase_size) + { + ssize_t len = read(fd, pSrcWrite->buffer + blockBytesRead, + pSrcPart->erase_size - blockBytesRead); + if (len < 0) + { + close(fd); + bml_over_mtd_write_close(pSrcWrite); + bml_over_mtd_write_close(pResWrite); + fprintf(stderr, "flash_bml_partition: read source file failed\n"); + return -1; + } + if (len == 0) + { + //End of file + break; + } + + blockBytesRead += len; + } + + + + int srcFd = -1; + if (blockMapping[currblock] == 0xffff) + { + //Good block, use src partition + srcFd = pSrcWrite->fd; + if (lseek64(pSrcWrite->fd, currblock*pSrcPart->erase_size, SEEK_SET)==-1) + { + close(fd); + bml_over_mtd_write_close(pSrcWrite); + bml_over_mtd_write_close(pResWrite); + fprintf(stderr, "flash_bml_partition: lseek in src partition failed\n"); + return -1; + } + } else + { + //Bad block, use mapped block in reservoir partition + srcFd = pResWrite->fd; + if (lseek64(pResWrite->fd, blockMapping[currblock]*pSrcPart->erase_size, SEEK_SET)==-1) + { + close(fd); + bml_over_mtd_write_close(pSrcWrite); + bml_over_mtd_write_close(pResWrite); + fprintf(stderr, "flash_bml_partition: lseek in reservoir partition failed\n"); + return -1; + } + } + size_t blockBytesWritten = 0; + while (blockBytesWritten < pSrcPart->erase_size) + { +#ifdef DUMMY_WRITING + ssize_t len = write(srcFd, pSrcWrite->buffer + blockBytesWritten, + pSrcPart->erase_size - blockBytesWritten); +#else + ssize_t len = bml_over_mtd_write_block(srcFd, pSrcPart->erase_size, pSrcWrite->buffer); +#endif + if (len <= 0) + { + close(fd); + bml_over_mtd_write_close(pSrcWrite); + bml_over_mtd_write_close(pResWrite); + fprintf(stderr, "flash_bml_partition: writing to partition failed\n"); + return -1; + } + blockBytesWritten += len; + } + + + } + + bml_over_mtd_write_close(pSrcWrite); + bml_over_mtd_write_close(pResWrite); + + if (close(fd)) { + printf("error closing %s", filename); + return -1; + } + + return 0; +} + +static int scan_partition(const MtdPartition* pPart) +{ + BmlOverMtdReadContext* readCtx = bml_over_mtd_read_partition(pPart); + if (readCtx == NULL) + { + fprintf(stderr, "Failed to open partition for reading.\n"); + return -1; + } + + int numBadBlocks = 0; + size_t numBlocks = pPart->size / pPart->erase_size; + size_t currBlock; + for (currBlock = 0; currBlock < numBlocks; ++currBlock) + { + + loff_t pos = currBlock * pPart->erase_size; + int mgbb = ioctl(readCtx->fd, MEMGETBADBLOCK, &pos); + if (mgbb != 0) + { + printf("Bad block %d at 0x%x.\n", currBlock, (unsigned int)pos); + numBadBlocks++; + } + } + + bml_over_mtd_read_close(readCtx); + if (numBadBlocks == 0) + { + printf("No bad blocks.\n"); + return 0; + } + return -1 ; +} + +int main(int argc, char **argv) +{ + if (argc != 7 && (argc != 3 || (argc == 3 && strcmp(argv[1],"scan"))!=0) + && (argc != 6 || (argc == 6 && strcmp(argv[1],"scan"))!=0)) + return die("Usage: %s dump|flash <partition> <partition_start_block> <reservoirpartition> <reservoir_start_block> <file>\n" + "E.g. %s dump boot 72 reservoir 2004 file.bin\n" + "Usage: %s scan <partition> [<partition_start_block> <reservoirpartition> <reservoir_start_block>]\n" + ,argv[0], argv[0], argv[0]); + int num_partitions = mtd_scan_partitions(); + const MtdPartition *pSrcPart = mtd_find_partition_by_name(argv[2]); + if (pSrcPart == NULL) + return die("Cannot find partition %s", argv[2]); + + int scanResult = scan_partition(pSrcPart); + + if (argc == 3 && strcmp(argv[1],"scan")==0) + { + return (scanResult == 0 ? 0 : EXIT_CODE_BAD_BLOCKS); + } + + int retVal = 0; + const MtdPartition* pReservoirPart = mtd_find_partition_by_name(argv[4]); + if (pReservoirPart == NULL) + return die("Cannot find partition %s", argv[4]); + + int srcPartStartBlock = atoi(argv[3]); + int reservoirPartStartBlock = atoi(argv[5]); + const unsigned short* pMapping = CreateBlockMapping(pSrcPart, srcPartStartBlock, + pReservoirPart, reservoirPartStartBlock); + + if (pMapping == NULL && scanResult == 0) + { + printf("Generating empty block mapping table for error-free partition.\n"); + pMapping = CreateEmptyBlockMapping(pSrcPart); + } + + if (argc == 6 && strcmp(argv[1],"scan")==0) + { + retVal = (scanResult == 0 ? 0 : EXIT_CODE_BAD_BLOCKS); + } + + if (pMapping == NULL) + return die("Failed to create block mapping table"); + + if (strcmp(argv[1],"dump")==0) + { + retVal = dump_bml_partition(pSrcPart, pReservoirPart, pMapping, argv[6]); + if (retVal == 0) + printf("Successfully dumped partition to %s\n", argv[6]); + } + + if (strcmp(argv[1],"flash")==0) + { + retVal = flash_bml_partition(pSrcPart, pReservoirPart, pMapping, argv[6]); + if (retVal == 0) + printf("Successfully wrote %s to partition\n", argv[6]); + + } + + + ReleaseBlockMapping(pMapping); + return retVal; +} + diff --git a/mtdutils/mounts.c b/mtdutils/mounts.c index 6a9b03d30..cd3738a0b 100644 --- a/mtdutils/mounts.c +++ b/mtdutils/mounts.c @@ -14,12 +14,12 @@ * limitations under the License. */ -#include <mntent.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <errno.h> +#include <unistd.h> #include <sys/mount.h> #include "mounts.h" @@ -60,8 +60,10 @@ free_volume_internals(const MountedVolume *volume, int zero) int scan_mounted_volumes() { - FILE* fp; - struct mntent* mentry; + char buf[2048]; + const char *bufp; + int fd; + ssize_t nbytes; if (g_mounts_state.volumes == NULL) { const int numv = 32; @@ -83,20 +85,80 @@ scan_mounted_volumes() } g_mounts_state.volume_count = 0; - /* Open and read mount table entries. */ - fp = setmntent(PROC_MOUNTS_FILENAME, "r"); - if (fp == NULL) { - return -1; + /* Open and read the file contents. + */ + fd = open(PROC_MOUNTS_FILENAME, O_RDONLY); + if (fd < 0) { + goto bail; + } + nbytes = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (nbytes < 0) { + goto bail; } - while ((mentry = getmntent(fp)) != NULL) { - MountedVolume* v = &g_mounts_state.volumes[g_mounts_state.volume_count++]; - v->device = strdup(mentry->mnt_fsname); - v->mount_point = strdup(mentry->mnt_dir); - v->filesystem = strdup(mentry->mnt_type); - v->flags = strdup(mentry->mnt_opts); + buf[nbytes] = '\0'; + + /* Parse the contents of the file, which looks like: + * + * # cat /proc/mounts + * rootfs / rootfs rw 0 0 + * /dev/pts /dev/pts devpts rw 0 0 + * /proc /proc proc rw 0 0 + * /sys /sys sysfs rw 0 0 + * /dev/block/mtdblock4 /system yaffs2 rw,nodev,noatime,nodiratime 0 0 + * /dev/block/mtdblock5 /data yaffs2 rw,nodev,noatime,nodiratime 0 0 + * /dev/block/mmcblk0p1 /sdcard vfat rw,sync,dirsync,fmask=0000,dmask=0000,codepage=cp437,iocharset=iso8859-1,utf8 0 0 + * + * The zeroes at the end are dummy placeholder fields to make the + * output match Linux's /etc/mtab, but don't represent anything here. + */ + bufp = buf; + while (nbytes > 0) { + char device[64]; + char mount_point[64]; + char filesystem[64]; + char flags[128]; + int matches; + + /* %as is a gnu extension that malloc()s a string for each field. + */ + matches = sscanf(bufp, "%63s %63s %63s %127s", + device, mount_point, filesystem, flags); + + if (matches == 4) { + device[sizeof(device)-1] = '\0'; + mount_point[sizeof(mount_point)-1] = '\0'; + filesystem[sizeof(filesystem)-1] = '\0'; + flags[sizeof(flags)-1] = '\0'; + + MountedVolume *v = + &g_mounts_state.volumes[g_mounts_state.volume_count++]; + v->device = strdup(device); + v->mount_point = strdup(mount_point); + v->filesystem = strdup(filesystem); + v->flags = strdup(flags); + } else { +printf("matches was %d on <<%.40s>>\n", matches, bufp); + } + + /* Eat the line. + */ + while (nbytes > 0 && *bufp != '\n') { + bufp++; + nbytes--; + } + if (nbytes > 0) { + bufp++; + nbytes--; + } } - endmntent(fp); + return 0; + +bail: +//TODO: free the strings we've allocated. + g_mounts_state.volume_count = 0; + return -1; } const MountedVolume * diff --git a/mtdutils/mounts.h b/mtdutils/mounts.h index d721355b8..30b2927c2 100644 --- a/mtdutils/mounts.h +++ b/mtdutils/mounts.h @@ -17,10 +17,6 @@ #ifndef MTDUTILS_MOUNTS_H_ #define MTDUTILS_MOUNTS_H_ -#ifdef __cplusplus -extern "C" { -#endif - typedef struct MountedVolume MountedVolume; int scan_mounted_volumes(void); @@ -34,8 +30,4 @@ int unmount_mounted_volume(const MountedVolume *volume); int remount_read_only(const MountedVolume* volume); -#ifdef __cplusplus -} -#endif - #endif // MTDUTILS_MOUNTS_H_ diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c index cd4f52cd5..6779d6e9a 100644 --- a/mtdutils/mtdutils.c +++ b/mtdutils/mtdutils.c @@ -28,12 +28,9 @@ #include "mtdutils.h" -struct MtdPartition { - int device_index; - unsigned int size; - unsigned int erase_size; - char *name; -}; +#ifdef RK3X + #include "rk3xhack.h" +#endif struct MtdReadContext { const MtdPartition *partition; @@ -327,8 +324,8 @@ static int read_block(const MtdPartition *partition, int fd, char *data) ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len) { - size_t read = 0; - while (read < len) { + ssize_t read = 0; + while (read < (int) len) { if (ctx->consumed < ctx->partition->erase_size) { size_t avail = ctx->partition->erase_size - ctx->consumed; size_t copy = len - read < avail ? len - read : avail; @@ -344,12 +341,12 @@ ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len) read += ctx->partition->erase_size; } - if (read >= len) { + if (read >= (int)len) { return read; } // Read the next block into the buffer - if (ctx->consumed == ctx->partition->erase_size && read < len) { + if (ctx->consumed == ctx->partition->erase_size && read < (int) len) { if (read_block(ctx->partition, ctx->fd, ctx->buffer)) return -1; ctx->consumed = 0; } @@ -432,11 +429,19 @@ static int write_block(MtdWriteContext *ctx, const char *data) erase_info.length = size; int retry; for (retry = 0; retry < 2; ++retry) { +#ifdef RK3X + if (rk30_zero_out(fd, pos, size) < 0) { + fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n", + pos, strerror(errno)); + continue; + } +#else if (ioctl(fd, MEMERASE, &erase_info) < 0) { printf("mtd: erase failure at 0x%08lx (%s)\n", pos, strerror(errno)); continue; } +#endif if (TEMP_FAILURE_RETRY(lseek(fd, pos, SEEK_SET)) != pos || TEMP_FAILURE_RETRY(write(fd, data, size)) != size) { printf("mtd: write error at 0x%08lx (%s)\n", @@ -466,7 +471,12 @@ static int write_block(MtdWriteContext *ctx, const char *data) // Try to erase it once more as we give up on this block add_bad_block_offset(ctx, pos); printf("mtd: skipping write block at 0x%08lx\n", pos); +#ifdef RK3X + rk30_zero_out(fd, pos, size); +#else + ioctl(fd, MEMERASE, &erase_info); +#endif pos += partition->erase_size; } @@ -539,9 +549,15 @@ off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks) struct erase_info_user erase_info; erase_info.start = pos; erase_info.length = ctx->partition->erase_size; +#ifdef RK3X + if (rk30_zero_out(ctx->fd, pos, ctx->partition->erase_size) < 0) { + fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos); + } +#else if (ioctl(ctx->fd, MEMERASE, &erase_info) < 0) { printf("mtd: erase failure at 0x%08lx\n", pos); } +#endif pos += ctx->partition->erase_size; } @@ -559,3 +575,224 @@ int mtd_write_close(MtdWriteContext *ctx) free(ctx); return r; } + +/* Return the offset of the first good block at or after pos (which + * might be pos itself). + */ +off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) { + int i; + for (i = 0; i < ctx->bad_block_count; ++i) { + if (ctx->bad_block_offsets[i] == pos) { + pos += ctx->partition->erase_size; + } else if (ctx->bad_block_offsets[i] > pos) { + return pos; + } + } + return pos; +} + +#define BLOCK_SIZE 2048 +#define SPARE_SIZE (BLOCK_SIZE >> 5) +#define HEADER_SIZE 2048 + +int cmd_mtd_restore_raw_partition(const char *partition_name, const char *filename) +{ + const MtdPartition *ptn; + MtdWriteContext *write; + void *data; + + FILE* f = fopen(filename, "rb"); + if (f == NULL) { + fprintf(stderr, "error opening %s", filename); + return -1; + } + + if (mtd_scan_partitions() <= 0) + { + fprintf(stderr, "error scanning partitions"); + return -1; + } + const MtdPartition *mtd = mtd_find_partition_by_name(partition_name); + if (mtd == NULL) + { + fprintf(stderr, "can't find %s partition", partition_name); + return -1; + } + + int fd = open(filename, O_RDONLY); + if (fd < 0) + { + printf("error opening %s", filename); + return -1; + } + + MtdWriteContext* ctx = mtd_write_partition(mtd); + if (ctx == NULL) { + printf("error writing %s", partition_name); + return -1; + } + + int success = 1; + char* buffer = malloc(BUFSIZ); + int read; + while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) { + int wrote = mtd_write_data(ctx, buffer, read); + success = success && (wrote == read); + } + free(buffer); + fclose(f); + + if (!success) { + fprintf(stderr, "error writing %s", partition_name); + return -1; + } + + if (mtd_erase_blocks(ctx, -1) == -1) { + fprintf(stderr, "error erasing blocks of %s\n", partition_name); + } + if (mtd_write_close(ctx) != 0) { + fprintf(stderr, "error closing write of %s\n", partition_name); + } + printf("%s %s partition\n", success ? "wrote" : "failed to write", partition_name); + return 0; +} + + +int cmd_mtd_backup_raw_partition(const char *partition_name, const char *filename) +{ + MtdReadContext *in; + const MtdPartition *partition; + char buf[BLOCK_SIZE + SPARE_SIZE]; + size_t partition_size; + size_t read_size; + size_t total; + int fd; + int wrote; + int len; + + if (mtd_scan_partitions() <= 0) + { + printf("error scanning partitions"); + return -1; + } + + partition = mtd_find_partition_by_name(partition_name); + if (partition == NULL) + { + printf("can't find %s partition", partition_name); + return -1; + } + + if (mtd_partition_info(partition, &partition_size, NULL, NULL)) { + printf("can't get info of partition %s", partition_name); + return -1; + } + + if (!strcmp(filename, "-")) { + fd = fileno(stdout); + } + else { + fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666); + } + + if (fd < 0) + { + printf("error opening %s", filename); + return -1; + } + + in = mtd_read_partition(partition); + if (in == NULL) { + close(fd); + unlink(filename); + printf("error opening %s: %s\n", partition_name, strerror(errno)); + return -1; + } + + total = 0; + while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) { + wrote = write(fd, buf, len); + if (wrote != len) { + close(fd); + unlink(filename); + printf("error writing %s", filename); + return -1; + } + total += BLOCK_SIZE; + } + + mtd_read_close(in); + + if (close(fd)) { + unlink(filename); + printf("error closing %s", filename); + return -1; + } + return 0; +} + +int cmd_mtd_erase_raw_partition(const char *partition_name) +{ + MtdWriteContext *out; + size_t erased; + size_t total_size; + size_t erase_size; + + if (mtd_scan_partitions() <= 0) + { + printf("error scanning partitions"); + return -1; + } + const MtdPartition *p = mtd_find_partition_by_name(partition_name); + if (p == NULL) + { + printf("can't find %s partition", partition_name); + return -1; + } + + out = mtd_write_partition(p); + if (out == NULL) + { + printf("could not estabilish write context for %s", partition_name); + return -1; + } + + // do the actual erase, -1 = full partition erase + erased = mtd_erase_blocks(out, -1); + + // erased = bytes erased, if zero, something borked + if (!erased) + { + printf("error erasing %s", partition_name); + return -1; + } + + return 0; +} + +int cmd_mtd_erase_partition(const char *partition, const char *filesystem) +{ + return cmd_mtd_erase_raw_partition(partition); +} + + +int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only) +{ + mtd_scan_partitions(); + const MtdPartition *p; + p = mtd_find_partition_by_name(partition); + if (p == NULL) { + return -1; + } + return mtd_mount_partition(p, mount_point, filesystem, read_only); +} + +int cmd_mtd_get_partition_device(const char *partition, char *device) +{ + mtd_scan_partitions(); + MtdPartition *p = mtd_find_partition_by_name(partition); + if (p == NULL) + return -1; + sprintf(device, "/dev/block/mtdblock%d", p->device_index); + return 0; +} diff --git a/mtdutils/mtdutils.h b/mtdutils/mtdutils.h index 8059d6a4d..6cbf37eec 100644 --- a/mtdutils/mtdutils.h +++ b/mtdutils/mtdutils.h @@ -19,10 +19,6 @@ #include <sys/types.h> // for size_t, etc. -#ifdef __cplusplus -extern "C" { -#endif - typedef struct MtdPartition MtdPartition; int mtd_scan_partitions(void); @@ -55,8 +51,11 @@ ssize_t mtd_write_data(MtdWriteContext *, const char *data, size_t data_len); off_t mtd_erase_blocks(MtdWriteContext *, int blocks); /* 0 ok, -1 for all */ int mtd_write_close(MtdWriteContext *); -#ifdef __cplusplus -} -#endif +struct MtdPartition { + int device_index; + unsigned int size; + unsigned int erase_size; + char *name; +}; #endif // MTDUTILS_H_ diff --git a/mtdutils/rk3xhack.c b/mtdutils/rk3xhack.c new file mode 100644 index 000000000..930f60593 --- /dev/null +++ b/mtdutils/rk3xhack.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013, Sergey 'Jin' Bostandzhyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This is a hack for Rockchip rk3x based devices. The problem is that + * the MEMERASE ioctl is failing (hangs and never returns) in their kernel. + * The sources are not fully available, so fixing it in the rk30xxnand_ko driver + * is not possible. + * + * I straced the stock recovery application and it seems to avoid this + * particular ioctl, instead it is simply writing zeroes using the write() call. + * + * This workaround does the same and will replace all MEMERASE occurances in + * the recovery code. + */ + +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +#include "rk3xhack.h" + +int rk30_zero_out(int fd, off_t pos, ssize_t size) +{ + if (lseek(fd, pos, SEEK_SET) != pos) { + fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n", + pos, strerror(errno)); + return -1; + } + + unsigned char *zb = (unsigned char *)calloc(1, size); + if (zb == NULL) { + fprintf(stderr, "mtd: erase failure, could not allocate memory\n"); + return -1; + } + + if (write(fd, zb, size) != size) { + fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n", + pos, strerror(errno)); + free(zb); + return -1; + } + + free(zb); + return 0; +} diff --git a/mtdutils/rk3xhack.h b/mtdutils/rk3xhack.h new file mode 100644 index 000000000..3cc16e49e --- /dev/null +++ b/mtdutils/rk3xhack.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013 Sergey 'Jin' Bostandzhyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This is a hack for Rockchip rk3x based devices. The problem is that + * the MEMERASE ioctl is failing (hangs and never returns) in their kernel. + * The sources are not fully available, so fixing it in the rk30xxnand_ko driver + * is not possible. + * + * I straced the stock recovery application and it seems to avoid this + * particular ioctl, instead it is simply writing zeroes using the write() call. + * + * This workaround does the same and will replace all MEMERASE occurances in + * the recovery code. + */ + +#ifndef __RK3X_HACK_H__ +#define __RK3X_HACK_H__ + +#include <sys/types.h> // for size_t, etc. + +// write zeroes to fd at position pos +int zero_out(int fd, off_t pos, ssize_t length); + +#endif//__RK3X_HACK_H__ |