/* * update_flash.c * * Copyright (C) 2006 by Digi International Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. */ /* * * !Revision: $Revision: 1.25 $ * !Descr: Flash Test Util * !References: [1] mtd-utils/flash_eraseall.c * [2] http://www.linux-mtd.infradead.org/doc/nand.html * [3] http://www.linux-mtd.infradead.org/tech/mtdnand/x255.html */ #define _XOPEN_SOURCE 500 /* for pread/pwrite */ #include /* ENOENT */ #include /* open */ #include /* basename */ #include /* regexp */ #include /* kill */ #include /* varg */ #include /* printf */ #include /* EXIT_SUCCESS */ #include /* memset */ #include /* close */ #include /* reboot */ #include /* ntohl */ #include /* setmntent */ #include /* ioctl */ #include /* mount */ #include /* stat */ #include /* statfs */ #include /* MEMERASE */ static int target_endian = __BYTE_ORDER; /* for jffs2-user.h, cpu_to_je16 */ #include "jffs2-user.h" /* jffs2_unknown_node */ /* libdigi */ #include #include #include #include #include #include #define VERSION "1.25" "-g"GIT_SHA1 /* man statfs does mention them, but they are only defined inside kernel */ #define NFS_SUPER_MAGIC 0x6969 /* linux/nfs_fs.h */ #define JFFS2_SUPER_MAGIC 0x72b6 /* linux/jffs2.h */ #define IO_BLOCK_SIZE 65536 #define FLASH_ERASED_BYTE 0xff #define WARNING "!!! " #define INFO "--- " #define PRINTF(...) \ do { \ if (!cSilent) \ printf(__VA_ARGS__); \ } while (0) #define PERCENTAGE(iCurrent, iTotal) ((iCurrent * 100) / (iTotal ? iTotal : 1)) #define SET_CRC32(pMtd, uiCRC32) \ do { \ pMtd->uiCRC32 = uiCRC32; \ pMtd->cChecksumSet = 1; \ } while (0) /* open it at least read-only so we can see whether open fails or not */ #define OPEN_READWRITE_IF_NOT_DRY ((cDryRun ? O_RDONLY : O_RDWR) | O_SYNC) /* ********** data types ********** */ typedef enum { PTUBoot = 0, PTKernel, PTEnvironment, PTFPGA, PTBootstream, PTUnknown /* always last */ } PartType_e; typedef enum { FTUBoot = 0, FTKernel, FTFPGA, FTNVRAM, FTJFFS2, FTSQUASHFS, FTUBI, FTBootstream, FTUnknown /* always last */ } FileType_e; typedef struct { /* configuration data */ const char *szOrigImageFileName; const char *szImageFileName; /* may later be /tmp/ */ unsigned int uiPartition; char cChecksumSet; char cEraseAll; uint32_t uiCRC32; /* auto-detected */ char cChecksumCalculated; char acName[64]; char cIsNAND; char cWriteCleanMarker; char cFileType; char cIsJFFS2; loff_t iSize; loff_t iFileSize; size_t iPageSize; mtd_info_t xInfo; unsigned int uiBadBlocks; PartType_e ePartType; FileType_e eFileType; FileType_e eFileTypeNeeded; /* open handles */ int iFd; /* of Partition */ /* status */ char cAlreadyPrintedVerifyWarning; char cAlreadyRemounted; unsigned int uiClMPos; unsigned int uiClMLen; struct jffs2_unknown_node xCleanMarker; } mtdPartition_t; /* ********** function definitions ********** */ /* top level functions */ static void DoPrintChecksums(void); static void DoMtdUpdate(void); static void DoMtdVerify(void); static void OnExit(void); /* helper functions */ static void MtdInit(void); static void MtdPartInit( /*@out@ */ mtdPartition_t * pMtd, unsigned int uiPartition, const char *szImageFileName); static void MtdPartOpen( /*@inout@ */ mtdPartition_t * pMtd, char cReadOnly); static void MtdPartClose( /*@inout@ */ mtdPartition_t * pMtd); static int MtdPartIsBadBlock(const mtdPartition_t * pMtd, loff_t iOffset); static void MtdPartUseFile( /*@inout@ */ mtdPartition_t * pMtd, const char *szImageFileName); static void MtdPartErase(const mtdPartition_t * pMtd); static void MtdPartWrite(mtdPartition_t * pMtd); static int MtdPartVerify(mtdPartition_t * pMtd); static void MtdPartCheckCRC32(mtdPartition_t * pMtd); static void MtdPartCopyFile(mtdPartition_t * pMtd, const char *szDstFileName); static void MtdPartInitCleanMarker(const mtdPartition_t * pMtd, struct jffs2_unknown_node *pCleanMarker, unsigned int *puiClMPos, unsigned int *puiClMLen); static void MtdPartInitJFFS2Node(struct jffs2_unknown_node *pNode, unsigned short uhNodeType, size_t iLen); static void MtdPartCompareCRC32(mtdPartition_t * pMtd, uint32_t uiCRC32); static void MtdPartRemountAllReadOnly(mtdPartition_t * pMtd); static void MtdPartDeterminePartType(mtdPartition_t * pMtd); static void MtdPartDetermineAndCheckFileType(mtdPartition_t * pMtd); static void MtdPartVerifyFile(mtdPartition_t * pMtd); static void MtdPartVerifyJFFS2Block(mtdPartition_t * pMtd, unsigned char *pucData, size_t iSize); static int MtdPartGetThrottle(const mtdPartition_t * pMtd, uint64_t ullSize); static void PrintProgress(int iPercentage, int iThrottle, const char *szFmt, ...); static void VerifyTmpDir(void); static uint32_t CalcCRC32OfFile(const char *szFileName); static const char *GetRootDevice(void); static void mtd_part_write_ubi(mtdPartition_t * pMtd); /* ********** local variables ********** */ /* set by command line */ static const char *szTmpDir = NULL; static const char *szKey = NULL; static char cNoImageTypeCheck = 0; static char cProgressInNewLine = 0; static char cSilent = 0; static char cChecksumOnly = 0; static char cDoReboot = 0; static char cHasChecksum = 0; static char cWriteCleanMarker = 0; static char cDryRun = 0; static char cVerify = 0; static char cVerifyOnly = 0; static char cEraseAll = 0; static char cMaxRetries = 3; static char cMarkBadBlocks = 0; /* calculated */ static mtdPartition_t axMtdParts[64]; static mtdPartition_t *pMtdPartLastToUpdate = axMtdParts; static const char *szMtdPrefix = "/dev/mtd/"; static const char *szMtdBlockPrefix = "/dev/mtdblock/"; static unsigned int uiMtdPartsCount = 0; #define MK(x, szName)[x] = szName static const char *aszPartType[PTUnknown + 1] = { MK(PTUBoot, "U-Boot"), MK(PTKernel, "Kernel"), MK(PTEnvironment, "NVRAM"), MK(PTFPGA, "FPGA"), MK(PTBootstream, "Bstrm-U-Boot"), MK(PTUnknown, "Unknown"), }; #undef MK #define MK(x, szRegExp, szName)[x] = {szRegExp, szName} static const struct { const char *szExp; const char *szName; } axFileType[FTUnknown + 1] = { MK(FTUBoot, "u-boot-.*\\.bin", "UBoot"), MK(FTNVRAM, "nvram-.*", "NVRAM"), MK(FTKernel, "uImage-.*", "Kernel"), MK(FTFPGA, ".*\\.biu", "FPGA"), MK(FTJFFS2, ".*\\.jffs2", "JFFS2"), MK(FTSQUASHFS, ".*\\.squashfs", "SQUASHFS"), MK(FTUBI, ".*\\.ubi", "UBI"), MK(FTBootstream, ".*\\.sb", "Bootstream"), MK(FTUnknown, ".*", "Unknown"), }; #undef MK /* ********** function implementations ********** */ int main(int argc, char *argv[]) { int iPartListIndex; CmdOptEntry aCmdEntries[] = { {COT_BOOL, 'C', &cChecksumOnly, "checksum-only", "calculates only CRC32 checksum of image"}, {COT_BOOL, 'R', &cDoReboot, "reboot", "reboots the system"}, {COT_BOOL, 'V', &cVerifyOnly, "verify-only", "verifies current contents, no updates are done"}, {COT_BOOL, 'v', &cVerify, "verify", "After flashing, compare flash contents with image on byte-to-byte"}, {COT_BOOL, 'c', &cHasChecksum, "checksum", "flashes only when checksum matches"}, {COT_BOOL, -1, &cDryRun, "dry-run", "don't erase or write to the flash"}, {COT_BOOL, -1, &cProgressInNewLine, "progress-in-new-line", "each percentage is printed in an own line"}, {COT_BOOL, 'i', &cNoImageTypeCheck, "no-image-type-check", "doesn't check image type for partition"}, {COT_BOOL, 'f', &cEraseAll, "erase-all", "erases the partition, not only the parts being written"}, {COT_BOOL, -1, &cWriteCleanMarker, "clean-marker", "writes clean markers to every partition (implies -f)"}, {COT_BOOL, 'b' , &cMarkBadBlocks, "bad-block-marking", "On repeated error, marks block as bad."}, {COT_BOOL, 's', &cSilent, "silent", "Silent Mode"}, {COT_STRING, 't', &szTmpDir, "tmpdir", "copy files to temporary directory before flashing"}, {COT_STRING, 'k', &szKey, "encrypt_key", "Verify bootstream image against encryption key"}, {COT_MORE, 0, NULL, "", "file to flash to partition and check for checksum"}, {COT_NONE, 0, NULL, NULL, NULL}, }; CLEAR(axMtdParts); szCmdOptVersion = "Version: " VERSION ", compiled on " __DATE__ "," __TIME__; iPartListIndex = cmdOptParse(argc, argv, aCmdEntries, "Flash Update Tool\n\n" "Examples of use cases:\n" " update_flash rootfs-ccw9cjsnand-128.jffs2 4\n" " => updates partition /dev/mtd4 with rootfs image\n" "\n" " update_flash -C uImage-ccw9cjsnand\n" " => calculates file CRC32 only\n" "\n" " update_flash -c uImage-ccw9cjsnand 3 0x1051e3c9\n" " => updates partition /dev/mtd3 only if CRC32 of file is 0x1051e3c9\n" "\n" " update_flash uImage-ccw9cjsnand 3 rootfs-ccw9cjsnand-128.jffs2 4:\n" " => updates kernel at partition 3 and rootfs at partition 4\n" "\n" " update_flash u-boot-cpx2-ivt.sb 0 -k 48855699413545113545511513300447\n" " => updates bootstream file on partition 0 if the encryption key matches\n"); /* so we can close everything even on error() or on return of main */ atexit(OnExit); /* Force disable writing clean markers for platforms that require atomic * access to the OOB */ if (cWriteCleanMarker && is_nand_oob_atomic()) { PRINTF(WARNING "JFFS2 clean markers disabled for this platform\n"); cWriteCleanMarker = 0; } if (cWriteCleanMarker) cEraseAll = 1; if (!cChecksumOnly) MtdInit(); /* check what files to write to what partition */ while (iPartListIndex < argc) { unsigned int uiPartition = 0; if ((pMtdPartLastToUpdate - axMtdParts) >= ARRAY_SIZE(axMtdParts)) error("Too many partitions to update on command line"); if (iPartListIndex > (argc - (1 + (cChecksumOnly ? 0 : 1) + (cHasChecksum ? 1 : 0)))) error("Require filename%s%s", (cChecksumOnly ? "" : " and partition"), (cHasChecksum ? " and checksum" : "")); if (!cChecksumOnly) { /* check partition argument */ if (sscanf(argv[iPartListIndex + 1], "%u", &uiPartition) != 1) error("Wrong partition number\n"); if (uiPartition >= uiMtdPartsCount) error("Have only %u mtd partitions", uiMtdPartsCount); MtdPartInit(pMtdPartLastToUpdate, uiPartition, argv[iPartListIndex]); iPartListIndex++; } else { /* initialize it partly */ CLEAR(*pMtdPartLastToUpdate); pMtdPartLastToUpdate->iFd = -1; MtdPartUseFile(pMtdPartLastToUpdate, argv[iPartListIndex]); } if (cHasChecksum) { /* parse checksum argument */ const char *szCRC32 = argv[iPartListIndex + 1]; uint32_t uiCRC32; if (sscanf(szCRC32, "%x", &uiCRC32) != 1) error("Invalid Checksum: %s", szCRC32); SET_CRC32(pMtdPartLastToUpdate, uiCRC32); iPartListIndex++; } iPartListIndex++; pMtdPartLastToUpdate++; } /* while( iPartListIndex ) */ /* all command line parsing verifications complete */ /* report what will be done */ if (cNoImageTypeCheck || cDryRun || cWriteCleanMarker || cEraseAll) { PRINTF("\nEnabled command line options:\n"); if (cNoImageTypeCheck) PRINTF(INFO "do not check image type\n"); if (cDryRun) PRINTF(INFO "dry-run: flash content is not changed\n"); if (cWriteCleanMarker) PRINTF(INFO "write JFFS2 clean markers\n"); if (cEraseAll) PRINTF(INFO "erase complete partition\n"); PRINTF("\n"); } if (cChecksumOnly) DoPrintChecksums(); else if (cVerifyOnly) DoMtdVerify(); else DoMtdUpdate(); PRINTF("Done\n"); if (cDoReboot) { sync(); reboot(RB_AUTOBOOT); } /* may not be reached in case of kill */ return EXIT_SUCCESS; } /*********************************************************************** * !Function: DoPrintChecksums * !Descr: Calculates CRC32 of files (and compares them to -c option) ***********************************************************************/ static void DoPrintChecksums(void) { mtdPartition_t *pMtd = NULL; PRINTF("CRC32 Results:\n"); for (pMtd = axMtdParts; pMtd < pMtdPartLastToUpdate; pMtd++) { uint32_t uiCRC32 = 0; uiCRC32 = CalcCRC32OfFile(pMtd->szImageFileName); PRINTF(" %-20s : 0x%08x\n", pMtd->szImageFileName, uiCRC32); if (pMtd->cChecksumSet) MtdPartCompareCRC32(pMtd, uiCRC32); } } /*********************************************************************** * !Function: DoMtdUpdate * !Descr: Updates all flash partitions ***********************************************************************/ static void DoMtdUpdate(void) { mtdPartition_t *pMtd = NULL; if (NULL != szTmpDir) { /* checksum is checked or calculated while copying to tmp */ VerifyTmpDir(); /* copy all files to tmp before starting to update */ for (pMtd = axMtdParts; pMtd < pMtdPartLastToUpdate; pMtd++) { /* copy it to temp */ char acTmpFileName[256]; char *szFileName = strdup(pMtd->szImageFileName); snprintf(acTmpFileName, sizeof(acTmpFileName) - 1, "%s/%s", szTmpDir, basename(szFileName)); acTmpFileName[sizeof(acTmpFileName) - 1] = 0; FREE(szFileName); PRINTF("Copying %s to %s\n", pMtd->szImageFileName, szTmpDir); MtdPartCopyFile(pMtd, acTmpFileName); /* use temporary file name from name */ FREE(pMtd->szImageFileName); /* get rid of const* */ pMtd->szImageFileName = strdup(acTmpFileName); } } else if (cHasChecksum || !cNoImageTypeCheck) { /* verify checksum before starting to update */ PRINTF("Verifying File(s): "); for (pMtd = axMtdParts; pMtd < pMtdPartLastToUpdate; pMtd++) { PRINTF(" %s\n", pMtd->szImageFileName); MtdPartVerifyFile(pMtd); } } /* make it read-only so no one can destroy the data */ for (pMtd = axMtdParts; pMtd < pMtdPartLastToUpdate; pMtd++) MtdPartRemountAllReadOnly(pMtd); PRINTF("Updating:\n"); for (pMtd = axMtdParts; pMtd < pMtdPartLastToUpdate; pMtd++) { PRINTF(" %s (%lli KiB)\n", pMtd->szImageFileName, TO_KiB(pMtd->iFileSize)); MtdPartOpen(pMtd, 0); /* For UBI images 'ubiformat' takes care of erasing the partition */ if (pMtd->eFileType != FTUBI) { MtdPartErase(pMtd); } if (PTBootstream == pMtd->ePartType) { char cmd[1024]; /* Bootstream partitions must be updated by Freescale kobs-ng application */ /* Close open mtd device */ MtdPartClose(pMtd); /* Build command to call kobs-ng * Use 'strcat' because -O2 compiler optimization * creates problems with 'sprintf' */ strcpy(cmd, "kobs-ng init -w"); if (cVerify) strcat(cmd, " -c"); if (NULL != szKey) { strcat(cmd, " -k"); strcat(cmd, szKey); } strcat(cmd, " --chip_0_device_path="); strcat(cmd, pMtd->acName); strcat(cmd, " "); strcat(cmd, pMtd->szImageFileName); strcat(cmd, " > /dev/null"); if (!system(cmd)) { PRINTF("\r Flashing: complete \n"); if (cVerify) { PRINTF("\r Verifying: complete \n"); } } else { PRINTF("\r Flashing: FAILED! \n"); exit(EXIT_FAILURE); } } else if (pMtd->eFileType == FTUBI) { /* UBI images are flashed using 'ubiformat' command */ MtdPartClose(pMtd); mtd_part_write_ubi(pMtd); } else { MtdPartWrite(pMtd); if (cVerify) { if (!MtdPartVerify(pMtd)) exit(EXIT_FAILURE); /* if CRC32 is given, it has been already checked on the input files. And Mtd is now same to them. So no need to check CRC32 again. */ } else if (cHasChecksum) MtdPartCheckCRC32(pMtd); MtdPartClose(pMtd); } PRINTF(" CRC32: 0x%08x\n", pMtd->uiCRC32); } if (NULL != szTmpDir) { /* delete all temporary files */ for (pMtd = axMtdParts; pMtd < pMtdPartLastToUpdate; pMtd++) if (unlink(pMtd->szImageFileName)) { /* * Do not exit with error in case the file to remove does not * exist. * Try to address corner cases like CCORE_MX53_EXTENSIONS-170 * (using same file to flash several different partitions) */ if (ENOENT == errno) { systemLog("%s", pMtd->szImageFileName); } else { systemError("%s", pMtd->szImageFileName); } } } } /*********************************************************************** * !Function: DoMtdVerify * !Descr: Verifies the flash images ***********************************************************************/ static void DoMtdVerify(void) { mtdPartition_t *pMtd = NULL; PRINTF("Verifying Images:\n"); for (pMtd = axMtdParts; pMtd < pMtdPartLastToUpdate; pMtd++) { MtdPartOpen(pMtd, 1); MtdPartVerify(pMtd); MtdPartClose(pMtd); } } /*********************************************************************** * !Function: OnExit * !Descr: Closes all open mtd partitions ***********************************************************************/ static void OnExit(void) { mtdPartition_t *pMtd; for (pMtd = axMtdParts; pMtd < pMtdPartLastToUpdate; pMtd++) { if (-1 != pMtd->iFd) MtdPartClose(pMtd); FREE(pMtd->szImageFileName); } } /*********************************************************************** * !Function: MtdInit * !Descr: Checks where the partitions (devfs or not) and how many we have ***********************************************************************/ static void MtdInit(void) { struct stat xStat; CLEAR(xStat); /* determine whether we are /dev/mtd/ or /dev/mtd */ if (-1 == stat(szMtdPrefix, &xStat)) { /* not dev fs */ if (-1 == stat("/dev/mtd0", &xStat)) error("No MTD devices available"); szMtdPrefix = "/dev/mtd"; szMtdBlockPrefix = "/dev/mtdblock"; } /* determine number of partitions */ while (uiMtdPartsCount < ARRAY_SIZE(axMtdParts)) { char acName[64]; axMtdParts[uiMtdPartsCount].iFd = -1; /* not open yet */ sprintf(acName, "%s%u", szMtdPrefix, uiMtdPartsCount); if (-1 == stat(acName, &xStat)) break; uiMtdPartsCount++; } if (!uiMtdPartsCount) error("No MTD partitions found"); } /*********************************************************************** * !Function: MtdPartInit * !Descr: opens partition and initializes all sizes and bad blocks ***********************************************************************/ static void MtdPartInit( /*@out@ */ mtdPartition_t * pMtd, unsigned int uiPartition, const char *szImageFileName) { CLEAR(*pMtd); pMtd->iFd = -1; pMtd->uiPartition = uiPartition; /* open mtd (may be /dev/mtd0 or /dev/mtd/0) */ MtdPartOpen(pMtd, 0); /* read partition info */ if (ioctl(pMtd->iFd, MEMGETINFO, &pMtd->xInfo)) systemError("ioctl( MEMGETINFO )"); pMtd->iSize = pMtd->xInfo.size; pMtd->cIsNAND = (MTD_NANDFLASH == pMtd->xInfo.type); pMtd->uiBadBlocks = 0; pMtd->cEraseAll = cEraseAll; pMtd->cWriteCleanMarker = cWriteCleanMarker; if (pMtd->cIsNAND) { /* determine bad block count */ /* !TODO. in 2.6.18 there already exists mtd_ecc_stats/ECCGETLAYOUT */ loff_t iOffset = 0; while (iOffset < pMtd->xInfo.size) { if (MtdPartIsBadBlock(pMtd, iOffset)) { logMsg(LOG_HARDWARE1, "Bad Block 0x%08llx on partition %u", iOffset, pMtd->uiPartition); pMtd->uiBadBlocks++; } iOffset += pMtd->xInfo.erasesize; } } pMtd->iPageSize = (pMtd->cIsNAND ? pMtd->xInfo.writesize : pMtd->xInfo.erasesize); MtdPartDeterminePartType(pMtd); PRINTF("Partition %u is %s (%s)\n", pMtd->uiPartition, (pMtd->cIsNAND ? "NAND" : ((MTD_NORFLASH == pMtd->xInfo.type) ? "NOR" : "???")), (pMtd->ePartType != PTUnknown) ? aszPartType[pMtd->ePartType] : ""); PRINTF(" Full Size: %llu KiB\n", TO_KiB(pMtd->iSize)); if (pMtd->uiBadBlocks) { /* determine effective (good) size */ PRINTF(" %u bad blocks\n", pMtd->uiBadBlocks); pMtd->iSize -= pMtd->xInfo.erasesize * pMtd->uiBadBlocks; } PRINTF(" Good Size: %llu KiB\n", TO_KiB(pMtd->iSize)); if (!is_nand_oob_atomic()) MtdPartInitCleanMarker(pMtd, &pMtd->xCleanMarker, &pMtd->uiClMPos, &pMtd->uiClMLen); MtdPartUseFile(pMtd, szImageFileName); /* close it to not leave an unused file descriptor open too long. The partition info is not gonna change anyway. */ MtdPartClose(pMtd); if (!cVerifyOnly && pMtd->cIsJFFS2) { PRINTF(INFO "JFFS2 partition %u will be fully erased", pMtd->uiPartition); /* clean rootfs completely */ pMtd->cEraseAll = 1; if (!is_nand_oob_atomic()) { pMtd->cWriteCleanMarker = 1; PRINTF(" and clean markers written\n"); } else { PRINTF("\n"); } } } /*********************************************************************** * !Function: MtdPartOpen * !Descr: Opens the partition, either read only or read-writable ***********************************************************************/ static void MtdPartOpen( /*@inout@ */ mtdPartition_t * pMtd, char cReadOnly) { char cReallyReadOnly = cReadOnly || cVerifyOnly; if (-1 != pMtd->iFd) error("Partition %u already open", pMtd->uiPartition); sprintf(pMtd->acName, "%s%u", szMtdPrefix, pMtd->uiPartition); pMtd->iFd = open(pMtd->acName, (cReallyReadOnly ? O_RDONLY : OPEN_READWRITE_IF_NOT_DRY)); if (-1 == pMtd->iFd) systemError(": %s", pMtd->acName); } /*********************************************************************** * !Function: MtdPartClose * !Descr: closes the Mtd Partition ***********************************************************************/ static void MtdPartClose( /*@inout@ */ mtdPartition_t * pMtd) { CLOSE(pMtd->iFd); } /*********************************************************************** * !Function: MtdPartIsBadBlock * !Return: 1 if the block at iOffset is bad and mustn't be used * !TODO: on first run, all bad blocks can be stored in a "bad" list and * reused later to reduce kernel calls ***********************************************************************/ static int MtdPartIsBadBlock(const mtdPartition_t * pMtd, loff_t iOffset) { char cIsBad = 0; int iRes = ioctl(pMtd->iFd, MEMGETBADBLOCK, &iOffset); if (iRes > 0) cIsBad = 1; else if ((iRes < 0) && (ENOTSUP != errno)) /* if not supported (NOR), assume it is good */ systemError("ioctl( MEMGETBADBLOCK )"); return cIsBad; } /*********************************************************************** * !Function: MtdMarkBadBlock * !Return: 0 on success, <1 on error ***********************************************************************/ static int MtdMarkBadBlock(const mtdPartition_t * pMtd, loff_t iOffset) { PRINTF("Marking offset %d as bad\n",(int)iOffset); return ( ioctl(pMtd->iFd, MEMSETBADBLOCK, &iOffset) ); } /*********************************************************************** * !Function: MtdPartUseFile * !Descr: Checks whether szImageFileName can be used for updating. * E.g. if !cNoImageTypeCheck is set, the prefixes of the image * file names are checked. ***********************************************************************/ static void MtdPartUseFile( /*@inout@ */ mtdPartition_t * pMtd, const char *szImageFileName) { struct stat xStat; CLEAR(xStat); if (stat(szImageFileName, &xStat)) systemError("%s", szImageFileName); pMtdPartLastToUpdate->szOrigImageFileName = szImageFileName; pMtd->szImageFileName = strdup(szImageFileName); pMtd->iFileSize = xStat.st_size; pMtd->eFileType = FTUnknown; if (-1 != pMtd->iFd) { if (xStat.st_size > pMtd->iSize) error("File %s is %lu KiB, but partition %u has only %lu KiB good free", pMtd->szImageFileName, TO_KiB(xStat.st_size), pMtd->uiPartition, TO_KiB(pMtd->iSize)); if (!cNoImageTypeCheck) MtdPartDetermineAndCheckFileType(pMtd); } } /*********************************************************************** * !Function: MtdPartErase * !Descr: erases the flash partition. * !see [1] ***********************************************************************/ static void MtdPartErase(const mtdPartition_t * pMtd) { erase_info_t xErase; loff_t iEraseSize = 0; loff_t iBytesErasedTotal = 0; char cLastWasBad = 0; struct mtd_oob_buf xoob; int iThrottle = MtdPartGetThrottle(pMtd, pMtd->xInfo.erasesize); CLEAR(xErase); xErase.length = pMtd->xInfo.erasesize; /* bad sectors have already been removed in pMtd->iSize */ iEraseSize = (pMtd->cEraseAll ? pMtd->iSize : pMtd->iFileSize); CLEAR(xoob); xoob.length = pMtd->uiClMLen; xoob.ptr = (unsigned char *)&pMtd->xCleanMarker; while (iBytesErasedTotal < iEraseSize) { if (!MtdPartIsBadBlock(pMtd, xErase.start)) { PrintProgress((((iBytesErasedTotal) * 100) / iEraseSize), iThrottle, " Erasing%s %i KiB @ 0x%08x:", (pMtd->cWriteCleanMarker ? " (CM)" : ""), TO_KiB(xErase.length), xErase.start); if (!cDryRun && ioctl(pMtd->iFd, MEMERASE, &xErase)) systemError("ioctl(MEMERASE)"); iBytesErasedTotal += xErase.length; cLastWasBad = 0; if (!cDryRun && pMtd->cWriteCleanMarker) { /* write cleanmarker */ if (pMtd->cIsNAND) { xoob.start = xErase.start + pMtd->uiClMPos; if (ioctl(pMtd->iFd, MEMWRITEOOB, &xoob)) systemError("ioctl( MEMWRITEOOB )"); } else { /* the NOR image already contains them. */ if (pwrite(pMtd->iFd, &pMtd->xCleanMarker, sizeof(pMtd->xCleanMarker), xErase.start) != sizeof(pMtd->xCleanMarker)) systemError("pwrite"); } } } else { logMsg(LOG_HARDWARE1, "%s" WARNING "Skipping bad sector @ 0x%08x ", (!cLastWasBad ? "\r" : ""), xErase.start); cLastWasBad = 1; } xErase.start += xErase.length; } PRINTF("\r Erasing: complete \n"); } /*********************************************************************** * !Function: mtd_part_write_ubi * !Descr: writes an UBI image file to partition ***********************************************************************/ static void mtd_part_write_ubi(mtdPartition_t * pMtd) { char line[256]; FILE *fpin; int ret; snprintf(line, sizeof(line), "ubiformat %s -f %s -y -q 2>&1 >/dev/null", pMtd->acName, pMtd->szImageFileName); fpin = popen(line, "r"); if (fgets(line, sizeof(line) - 1, fpin) != NULL) { line[strlen(line) - 1] = 0; } ret = pclose(fpin); if (!WEXITSTATUS(ret)) { PRINTF("\r Flashing: complete \n"); } else { PRINTF("\r Flashing: FAILED! (%s)\n", line); exit(EXIT_FAILURE); } } /*********************************************************************** * !Function: MtdPartWrite * !Descr: writes the image file to partition ***********************************************************************/ static void MtdPartWrite(mtdPartition_t * pMtd) { loff_t iBytesReadTotal = 0; loff_t iCurrentOffs = 0; int iFdSrc = -1; char cLastWasBad = 0; int iBytesRead = 0; uint32_t uiCRC32 = 0; unsigned char *pucBuffer = NULL; const size_t iBlockSize = pMtd->iPageSize; int iThrottle = MtdPartGetThrottle(pMtd, pMtd->iFileSize); unsigned int i,iRet; unsigned int ref_uicrc32 = 0,new_uicrc32 = 0; unsigned char *pucBufferMtd = NULL; pucBuffer = (unsigned char *)malloc(iBlockSize); if (NULL == pucBuffer) systemError("malloc"); iFdSrc = open(pMtd->szImageFileName, O_RDONLY); if (-1 == iFdSrc) systemError("%s", pMtd->szImageFileName); /* rewind because descriptor was open and we don't know the state */ if (lseek(pMtd->iFd, 0, SEEK_SET)) systemError("%s", pMtd->acName); do { /* write one sector */ if (!MtdPartIsBadBlock(pMtd, iCurrentOffs)) { int iBytesWritten; PrintProgress(PERCENTAGE(iBytesReadTotal, pMtd->iFileSize), iThrottle, " Flashing:"); iBytesRead = read(iFdSrc, pucBuffer, iBlockSize); if (!iBytesRead) break; else if (-1 == iBytesRead) systemError("%s", pMtd->szImageFileName); if (!pMtd->cChecksumCalculated) uiCRC32 = crc32(uiCRC32, pucBuffer, iBytesRead); if (iBytesRead < iBlockSize) { char cEmptyChar = FLASH_ERASED_BYTE; char bJFFS2Padding = 0; if (pMtd->cIsJFFS2 && (iBytesRead + sizeof(struct jffs2_unknown_node) < iBlockSize)) { bJFFS2Padding = 1; cEmptyChar = 0; /* see wbuf.c */ } /* fill block with empty characters */ memset(pucBuffer + iBytesRead, cEmptyChar, iBlockSize - iBytesRead); if (bJFFS2Padding) { /* write padding to avoid Empty block messages. see linux/fs/jffs2/wbuf.c:flush_wbuf */ struct jffs2_unknown_node *pNode = (struct jffs2_unknown_node *)(pucBuffer + iBytesRead); logMsg(LOG_HARDWARE1, "\nPadding last sector"); MtdPartInitJFFS2Node(pNode, JFFS2_NODETYPE_PADDING, iBlockSize - iBytesRead); } } /* at least nand writes should be aligned */ if( !cDryRun && cMarkBadBlocks ) { ref_uicrc32 = crc32(0, pucBuffer, iBlockSize); pucBufferMtd = (unsigned char *)malloc(iBlockSize); if (NULL == pucBufferMtd) systemError("malloc"); } for( i = 0 ; i < cMaxRetries ; i++ ) { int iBytesReadMtd; iBytesWritten = (!cDryRun ? pwrite(pMtd->iFd, pucBuffer, iBlockSize, iCurrentOffs) : iBlockSize); if (iBytesWritten != iBlockSize) { if( !cDryRun && cMarkBadBlocks ) { PRINTF("[%s:%d] %s: Retrying failed write %d.\n", __FUNCTION__,__LINE__,pMtd->acName,i); continue; } else { systemError("%s", pMtd->acName); } } if (!cDryRun && cMarkBadBlocks) { iBytesReadMtd = pread(pMtd->iFd, pucBufferMtd, iBlockSize, iCurrentOffs); if ( iBytesReadMtd < 0 ) { PRINTF("[%s:%d] %s: Read error.\n", __FUNCTION__,__LINE__,pMtd->acName); continue; } new_uicrc32 = crc32(0, pucBufferMtd, iBlockSize); if( new_uicrc32 != ref_uicrc32 ) { PRINTF("[%s:%d] %s: CRC mismatch %08x <> %08x.\n", __FUNCTION__,__LINE__,pMtd->acName,ref_uicrc32, new_uicrc32); continue; } FREE(pucBufferMtd); } break; } if( !cDryRun && cMarkBadBlocks && (i >= cMaxRetries) ) { PRINTF("[%s:%d] %s: Marking as bad block.\n", __FUNCTION__,__LINE__,pMtd->acName); iRet = MtdMarkBadBlock( pMtd , iCurrentOffs ); systemError("%s: Bad block marking %s.", pMtd->acName, strerror(iRet)); } iBytesReadTotal += iBytesRead; cLastWasBad = 0; } else { logMsg(LOG_HARDWARE1, "%s" WARNING "Skipping bad sector @ 0x%08x ", (!cLastWasBad ? "\r" : ""), iCurrentOffs); cLastWasBad = 1; } iCurrentOffs += iBlockSize; } while (iCurrentOffs < pMtd->xInfo.size); CLOSE(iFdSrc); FREE(pucBuffer); if (pMtd->iFileSize != iBytesReadTotal) error("Filesize changed while updating: %s", pMtd->szImageFileName); if (!pMtd->cChecksumCalculated) { SET_CRC32(pMtd, uiCRC32); pMtd->cChecksumCalculated = 1; } PRINTF("\r Flashing: complete \n"); } /*********************************************************************** * !Function: MtdPartVerify * !Return: 1 if identical in the used sectors of szOrigImageFileName * otherwise 0 * !Descr: compares contents of MtdPartition with szOrigImageFileName * Only checks the last used sector whether the parts the source * file not uses are empty. Other blocks are not checked. ***********************************************************************/ static int MtdPartVerify(mtdPartition_t * pMtd) { unsigned char *pucBufferSrc; unsigned char *pucBufferMtd; int iFdSrc = -1; loff_t iBytesReadTotal = 0; loff_t iOffsMtd = 0; char cRes = 0; char cLastWasBad = 0; const size_t iBlockSize = pMtd->iPageSize; int iThrottle = MtdPartGetThrottle(pMtd, pMtd->iFileSize); unsigned int i,iRet; pucBufferSrc = (unsigned char *)malloc(iBlockSize); if (NULL == pucBufferSrc) systemError("malloc"); pucBufferMtd = (unsigned char *)malloc(iBlockSize); if (NULL == pucBufferMtd) systemError("malloc"); /* sync everything */ MtdPartClose(pMtd); MtdPartOpen(pMtd, 1); /* also rewinds read pointer */ iFdSrc = open(pMtd->szOrigImageFileName, O_RDONLY); if (-1 == iFdSrc) systemError("%s", pMtd->szOrigImageFileName); do { if (!MtdPartIsBadBlock(pMtd, iOffsMtd)) { int iBytesReadSrc; int iBytesReadMtd; loff_t iOffs; PrintProgress(PERCENTAGE(iBytesReadTotal, pMtd->iFileSize), iThrottle, " Verifying:"); iBytesReadSrc = read(iFdSrc, pucBufferSrc, iBlockSize); if (!iBytesReadSrc) /* nothing left */ break; else if (iBytesReadSrc < 0) systemError("%s", pMtd->szOrigImageFileName); for( i = 0 ; i < cMaxRetries ; i++ ) { iBytesReadMtd = pread(pMtd->iFd, pucBufferMtd, iBlockSize, iOffsMtd); if (iBytesReadMtd < 0) { if( !cDryRun && cMarkBadBlocks ) { printf("[%s:%d] %s: Retrying failed read %d, %s.\n", __FUNCTION__,__LINE__,pMtd->acName,i,strerror(errno)); continue; } else systemError("%s", pMtd->acName); } break; } if( !cDryRun && cMarkBadBlocks && (i >= cMaxRetries) ) { PRINTF("[%s:%d] %s: Marking as bad block.\n", __FUNCTION__,__LINE__,pMtd->acName); iRet = MtdMarkBadBlock( pMtd , iOffsMtd ); systemError("%s: Bad block marking %s.", pMtd->acName, strerror(iRet)); } if (iBytesReadSrc > iBytesReadMtd) { logMsg(LOG_ERR, "\nSize mismatch. Source has %i Bytes but flash only %i bytes", iBytesReadSrc, iBytesReadMtd); goto ret; } iOffs = MemCmp(pucBufferSrc, pucBufferMtd, iBytesReadSrc); if (-1 != iOffs) { logMsg(LOG_ERR, "\nData mismatch @ 0x%08x\n", iBytesReadTotal + iOffs); logMsg(LOG_ERR, "Source is"); MemDump(pucBufferSrc, iOffs & ~0xf, MIN(iBytesReadSrc, 0x20)); logMsg(LOG_ERR, "Flash is"); MemDump(pucBufferMtd, iOffs & ~0xf, MIN(iBytesReadMtd, 0x20)); goto ret; } if (!pMtd->cIsJFFS2 && (iBytesReadSrc < iBytesReadMtd)) { /* !TODO. JFFS2 is padded. This is not yet checked. */ int i; /* check for emptiness of block. */ /* !TODO. This works only for NOR and NAND */ for (i = iBytesReadSrc; i < iBytesReadMtd; i++) if (FLASH_ERASED_BYTE != pucBufferMtd[i]) { logMsg(LOG_ERR, "Mtd is not empty @ 0x%llx\n", iBytesReadTotal + i); goto ret; } } /* if( iBytesReadSrc < iBytesReadMtd */ iBytesReadTotal += iBytesReadSrc; cLastWasBad = 0; } else { logMsg(LOG_HARDWARE1, "%s" WARNING "Skipping bad sector @ 0x%08x ", (!cLastWasBad ? "\r" : ""), iOffsMtd); cLastWasBad = 1; } iOffsMtd += iBlockSize; } while (iOffsMtd < pMtd->xInfo.size); if (pMtd->iFileSize != iBytesReadTotal) { logMsg(LOG_ERR, "Filesize changed while updating: %s", pMtd->szImageFileName); goto ret; } PRINTF("\r Verifying: complete \n"); cRes = 1; ret: CLOSE(iFdSrc); FREE(pucBufferMtd); FREE(pucBufferSrc); return cRes; } /*********************************************************************** * !Function: MtdPartCheckCRC32 * !Descr: calculates checksum of mtd partition up to filesize ***********************************************************************/ static void MtdPartCheckCRC32(mtdPartition_t * pMtd) { unsigned char *pucBufferMtd; loff_t iBytesReadTotal = 0; loff_t iOffsMtd = 0; uint32_t uiCRC32 = 0; const size_t iBlockSize = pMtd->xInfo.erasesize; int iThrottle = MtdPartGetThrottle(pMtd, pMtd->iFileSize); pucBufferMtd = (unsigned char *)malloc(iBlockSize); if (NULL == pucBufferMtd) systemError("malloc"); /* sync everything */ MtdPartClose(pMtd); MtdPartOpen(pMtd, 1); /* also rewinds read pointer */ do { if (!MtdPartIsBadBlock(pMtd, iOffsMtd)) { int iBytesReadMtd; PrintProgress(PERCENTAGE(iBytesReadTotal, pMtd->iFileSize), iThrottle, " CRC32: "); iBytesReadMtd = pread(pMtd->iFd, pucBufferMtd, iBlockSize, iOffsMtd); if (iBytesReadMtd < 0) systemError("%s", pMtd->acName); if ((iBytesReadTotal + iBytesReadMtd) > pMtd->iFileSize) iBytesReadMtd = (pMtd->iFileSize - iBytesReadTotal); uiCRC32 = crc32(uiCRC32, pucBufferMtd, iBytesReadMtd); iBytesReadTotal += iBytesReadMtd; } iOffsMtd += iBlockSize; } while (pMtd->iFileSize > iBytesReadTotal); PRINTF("\r CRC32: complete \n"); FREE(pucBufferMtd); MtdPartCompareCRC32(pMtd, uiCRC32); } /*********************************************************************** * !Function: MtdPartCopyFile * !Descr: copies the image file to szDstFileName and calculates its * check sum ***********************************************************************/ static void MtdPartCopyFile(mtdPartition_t * pMtd, const char *szDstFileName) { unsigned char *pucBuffer = NULL; int iFdDst = -1; int iFdSrc = -1; loff_t iBytesReadTotal = 0; int iBytesRead; uint32_t uiCRC32 = 0; const size_t iBlockSize = pMtd->xInfo.erasesize; pucBuffer = (unsigned char *)malloc(iBlockSize); if (NULL == pucBuffer) systemError("malloc"); iFdSrc = open(pMtd->szImageFileName, O_RDONLY); if (-1 == iFdSrc) systemError("%s", pMtd->szImageFileName); iFdDst = open(szDstFileName, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (-1 == iFdDst) systemError("%s", szDstFileName); do { int iBytesWritten; iBytesRead = read(iFdSrc, pucBuffer, iBlockSize); if (!iBytesRead) break; else if (-1 == iBytesRead) systemError("%s", pMtd->szImageFileName); if (pMtd->cIsJFFS2) MtdPartVerifyJFFS2Block(pMtd, pucBuffer, iBytesRead); uiCRC32 = crc32(uiCRC32, pucBuffer, iBytesRead); iBytesWritten = write(iFdDst, pucBuffer, iBytesRead); if (iBytesWritten != iBytesRead) systemError("%s", szDstFileName); iBytesReadTotal += iBytesRead; } while (1); CLOSE(iFdDst); CLOSE(iFdSrc); FREE(pucBuffer); if (pMtd->iFileSize != iBytesReadTotal) error("Filesize changed while updating: %s", pMtd->szImageFileName); if (!pMtd->cChecksumSet) { SET_CRC32(pMtd, uiCRC32); pMtd->cChecksumCalculated = 1; } else MtdPartCompareCRC32(pMtd, uiCRC32); } /*********************************************************************** * !Function: MtdPartInitCleanMarker * !Descr: initializes the clean marker * !see [1] ***********************************************************************/ static void MtdPartInitCleanMarker(const mtdPartition_t * pMtd, struct jffs2_unknown_node *pCleanMarker, unsigned int *puiClMPos, unsigned int *puiClMLen) { *puiClMPos = 0; *puiClMLen = 8; if (pMtd->cIsNAND) { struct nand_oobinfo xoobInfo; CLEAR(xoobInfo); if (ioctl(pMtd->iFd, MEMGETOOBSEL, &xoobInfo)) systemError("ioctl( MEMGETOOBSEL )"); /* check for autoplacement */ if (MTD_NANDECC_AUTOPLACE == xoobInfo.useecc) { /* get the position of the free bytes */ if (!xoobInfo.oobfree[0][1]) error("Autoplacement selected and no empty space in oob\n"); *puiClMPos = xoobInfo.oobfree[0][0]; *puiClMLen = xoobInfo.oobfree[0][1]; if (*puiClMLen > 8) *puiClMLen = 8; else { /* legacy mode, detect autoplacement ourselves [3] */ switch (pMtd->xInfo.oobsize) { case 8: *puiClMPos = 6; *puiClMLen = 2; break; case 16: *puiClMPos = 8; *puiClMLen = 8; break; case 64: *puiClMPos = 16; *puiClMLen = 8; break; default: error("unsupported oobsize %i\n", pMtd->xInfo.oobsize); break; } /* switch */ } /* if( *piClMPos ) */ } /* if( NAND_AUTOPLACE */ } else MtdPartInitJFFS2Node(pCleanMarker, JFFS2_NODETYPE_CLEANMARKER, sizeof(struct jffs2_unknown_node)); logMsg(LOG_HARDWARE2, " OOB has %i bytes, CleanMarker is from 0x%02x to 0x%02x", pMtd->xInfo.oobsize, *puiClMPos, *puiClMPos + *puiClMLen); } /*********************************************************************** * !Function: MtdPartInitJFFS2Node * !Descr: initializes a node and generates hdr checksum ***********************************************************************/ static void MtdPartInitJFFS2Node(struct jffs2_unknown_node *pNode, unsigned short uhNodeType, size_t iLen) { uint32_t uiStart = 0xffffffff; /* JFFS CRC32 starts from 0xfffffff, our crc32 from 0x0 */ CLEAR(*pNode); pNode->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); pNode->nodetype = cpu_to_je16(uhNodeType); pNode->totlen = cpu_to_je32(iLen); /* don't CRC32 the hdr_crc itself */ pNode->hdr_crc = cpu_to_je32(crc32(uiStart, pNode, sizeof(struct jffs2_unknown_node) - 4) ^ uiStart); } /*********************************************************************** * !Function: MtdPartCompareCRC32 * !Descr: checks whether the CRC32 is same as previously calculated. ***********************************************************************/ static void MtdPartCompareCRC32(mtdPartition_t * pMtd, uint32_t uiCRC32) { if (uiCRC32 != pMtd->uiCRC32) error("CRC32 mismatch on %s.\n Expected 0x%08x, have 0x%08x\n", pMtd->szImageFileName, pMtd->uiCRC32, uiCRC32); } /*********************************************************************** * !Function: MtdPartRemountAllReadOnly * !Descr: remounts all uses of the partition as read-only ***********************************************************************/ static void MtdPartRemountAllReadOnly(mtdPartition_t * pMtd) { static const char *szMount = "/proc/mounts"; char acMtdBlock[20]; FILE *fhMount = NULL; const char *szRootDev = GetRootDevice(); if (pMtd->cAlreadyRemounted) /* nothing to do */ return; fhMount = setmntent(szMount, "r"); if (NULL == fhMount) systemError(szMount); sprintf(acMtdBlock, "%s%u", szMtdBlockPrefix, pMtd->uiPartition); /* scan all mount entries */ do { char bIsRootDev = 0; char bRemount = 0; struct mntent *pxEnt = getmntent(fhMount); if (NULL == pxEnt) break; bIsRootDev = (!strcmp(pxEnt->mnt_fsname, "/dev/root") && !strcmp(szRootDev, acMtdBlock)); if (!bIsRootDev && !strcmp(pxEnt->mnt_fsname, acMtdBlock)) { PRINTF(WARNING "Umounting %s\n", pxEnt->mnt_dir); if (umount(pxEnt->mnt_dir) || bIsRootDev) { /* rootdev can't be unmounted */ /* it's for our mtd partition, remount it */ PRINTF(WARNING "Failed, trying to remount read-only\n"); bRemount = 1; } } if (bIsRootDev || bRemount) { PRINTF(WARNING "Remounting %s\n", pxEnt->mnt_dir); if (mount(pxEnt->mnt_fsname, pxEnt->mnt_dir, NULL, MS_REMOUNT | MS_RDONLY, NULL)) { if (EBUSY == errno) error("Partition in use, can't update it\n"); else systemError("%s", pxEnt->mnt_fsname); } } } while (1); endmntent(fhMount); pMtd->cAlreadyRemounted = 1; } /*********************************************************************** * !Function: MtdPartDeterminePartType * !Descr: Determine partition type (rootfs, uboot, etc.) based on * partition name in /proc/mtd ***********************************************************************/ static void MtdPartDeterminePartType(mtdPartition_t * pMtd) { static const char *szMtd = "/proc/mtd"; FILE *fhMtd = NULL; unsigned int uiPart; char acBuffer[200]; int iPart; loff_t uiSize; loff_t uiEraseSize; char acName[200]; static const struct { PartType_e eType; FileType_e eFileType; const char *szName; } axTypes[] = { {PTUBoot, FTUBoot, "\"U-Boot"}, {PTKernel, FTKernel, "\"Kernel"}, {PTEnvironment, FTNVRAM, "\"NVRAM"}, {PTFPGA, FTFPGA, "\"FPGA"}, {PTBootstream, FTBootstream, "\"Bstrm-U-Boot"}, }; int i; /* open /proc/mtd */ fhMtd = fopen(szMtd, "r"); if (NULL == fhMtd) systemError(szMtd); /* skip table header (check return value to avoid compiler warning) */ if (fgets(acBuffer, sizeof(acBuffer), fhMtd)); /* seek partition description */ for (iPart = 0; iPart <= pMtd->uiPartition; iPart++) if (NULL == fgets(acBuffer, sizeof(acBuffer) - 1, fhMtd)) systemError(szMtd); /* break it into parts */ acBuffer[sizeof(acBuffer) - 1] = 0; if (sscanf(acBuffer, "mtd%u: %llx %llx %199s", &uiPart, &uiSize, &uiEraseSize, acName) != 4) error("Wrong /proc/mtd line: %s", acBuffer); if (uiPart != pMtd->uiPartition) error("Wrong partition: %s", acBuffer); /* determine partition type */ pMtd->ePartType = PTUnknown; pMtd->eFileTypeNeeded = FTUnknown; for (i = 0; i < ARRAY_SIZE(axTypes); i++) { if (!strncmp(axTypes[i].szName, acName, strlen(axTypes[i].szName))) { pMtd->ePartType = axTypes[i].eType; pMtd->eFileTypeNeeded = axTypes[i].eFileType; break; } } if (fclose(fhMtd) < 0) systemError(szMtd); } /*********************************************************************** * !Function: MtdPartDetermineAndCheckFileType * !Descr: Determine file type (rootfs, uboot, etc.) and checks whether it * is ok for the partition. Done by looking at filename ***********************************************************************/ static void MtdPartDetermineAndCheckFileType(mtdPartition_t * pMtd) { FileType_e eType = 0; int iMatch = 0; regex_t regexp; CLEAR(regexp); /* !TODO: Verify File Type, JFFS2, uimage, u-boot by looking into it */ while (eType < ARRAY_SIZE(axFileType)) { /* does axFileType[ eType ] matches pMtd->szImageFileName? */ int iError = regcomp(®exp, axFileType[eType].szExp, REG_NOSUB); if (iError) { char acError[200]; regerror(iError, ®exp, acError, sizeof(acError) - 1); error("%s", acError); } iMatch = !regexec(®exp, pMtd->szImageFileName, 0, NULL, 0); regfree(®exp); if (iMatch) { pMtd->eFileType = eType; logMsg(LOG_HARDWARE1, " Detected Image Type for %s: %s", pMtd->szImageFileName, axFileType[eType].szName); /* check whether it matches the partition */ if ((pMtd->eFileTypeNeeded != pMtd->eFileType) && (pMtd->eFileTypeNeeded != FTUnknown)) error("File %s (Type %s) doesn't match partition type %s", pMtd->szImageFileName, axFileType[pMtd->eFileType].szName, aszPartType[pMtd->ePartType]); if (FTJFFS2 == pMtd->eFileType) pMtd->cIsJFFS2 = 1; break; } eType++; } } /*********************************************************************** * !Function: MtdPartVerifyFile * !Descr: verifies the file (checksum, JFFS2) ***********************************************************************/ static void MtdPartVerifyFile(mtdPartition_t * pMtd) { unsigned char *pucBuffer = NULL; uint32_t uiCRC32 = 0; int iFd = -1; int iBytesRead; const size_t iBlockSize = pMtd->xInfo.erasesize; pucBuffer = (unsigned char *)malloc(iBlockSize); if (NULL == pucBuffer) systemError("malloc"); iFd = open(pMtd->szImageFileName, O_RDONLY); if (-1 == iFd) systemError("%s", pMtd->szImageFileName); do { iBytesRead = read(iFd, pucBuffer, iBlockSize); if (!iBytesRead) break; else if (-1 == iBytesRead) systemError("%s", pMtd->szImageFileName); if (pMtd->cIsJFFS2) MtdPartVerifyJFFS2Block(pMtd, pucBuffer, iBytesRead); uiCRC32 = crc32(uiCRC32, pucBuffer, iBytesRead); } while (1); CLOSE(iFd); FREE(pucBuffer); if (!pMtd->cChecksumSet) { SET_CRC32(pMtd, uiCRC32); pMtd->cChecksumCalculated = 1; } else MtdPartCompareCRC32(pMtd, uiCRC32); } /*********************************************************************** * !Function: MtdPartVerifyJFFS2Block * !Descr: verifies whether the JFFS2 block is correct. On NOR clean markers * need to be on the correct position, on NAND there mustn't be. ***********************************************************************/ static void MtdPartVerifyJFFS2Block(mtdPartition_t * pMtd, unsigned char *pucData, size_t iSize) { char cCleanMarkerPresent = 0; int iOffs; /* only magic and node, length field of xCleanMarker may vary */ static const size_t NODE_SIZE = 4; if (pMtd->cAlreadyPrintedVerifyWarning) /* do it only once */ return; if (iSize >= 4) cCleanMarkerPresent = (MemCmp(pucData, &pMtd->xCleanMarker, NODE_SIZE) == -1); if (pMtd->cIsNAND) { const jint16_t MAGIC = cpu_to_je16(JFFS2_MAGIC_BITMASK); if (cCleanMarkerPresent) { logMsg(LOG_ERR, WARNING "CleanMarkers present in image for NAND. JFFS2 will complain but function."); pMtd->cAlreadyPrintedVerifyWarning = 1; } /* MAGIC is < sizeof( xCleanMarker ) */ if (MemCmp(pucData, &MAGIC, sizeof(MAGIC)) != -1) error("No JFFS2 Header at erase block begin"); } else { /* NOR etc. */ if (!cCleanMarkerPresent) error ("No CleanMarkers present in image for NOR. JFFS2 won't like that. Possibly wrong erase block size of jffs2 or NAND image"); /* there shouldn't be any other clean marker in the block. Check 2^n offsets. */ for (iOffs = 16; iOffs < iSize - NODE_SIZE; iOffs *= 2) { if (MemCmp(pucData + iOffs, &pMtd->xCleanMarker, NODE_SIZE) == -1) error ("Clean Markers present in the block at offset 0x%08x. Possibly wrong erase block size of jffs2", iOffs); } } } /*********************************************************************** * !Function: MtdPartGetThrottle ***********************************************************************/ static int MtdPartGetThrottle(const mtdPartition_t * pMtd, uint64_t ullSize) { /* Eclipse has a nicer output with an update every second. Therefore it * is not throttled. */ return 1; } /*********************************************************************** * !Function: PrintProgress * !Descr: Prints message only when progress has changed ***********************************************************************/ static void PrintProgress(int iPercentage, int iThrottle, const char *szFmt, ...) { static int iLastPercentage = -1; int iThrottled = iPercentage / iThrottle; if (cSilent) /* nothing to print */ return; if (iThrottled != iLastPercentage) { va_list args; iLastPercentage = iThrottled; va_start(args, szFmt); vprintf(szFmt, args); printf("% 3i%% \r", iPercentage); va_end(args); if (cProgressInNewLine) PRINTF("\n"); fflush(stdout); } } /*********************************************************************** * !Function: VerifyTmpDir * !Descr: checks whether the temporary directory can be used ***********************************************************************/ static void VerifyTmpDir(void) { struct statfs xStat; CLEAR(xStat); if (statfs(szTmpDir, &xStat)) systemError("statfs"); switch (xStat.f_type) { case JFFS2_SUPER_MAGIC: /* flash image temporary on flash fs??? */ error("Makes no sense to store flash image temporarily on JFFS2"); break; /* not reached */ case NFS_SUPER_MAGIC: error("Makes no sense to store flash image temporarily on NFS"); break; /* not reached */ default: /* Accept all other ones. If they are read-only, it is detected before * erasing flash */ break; } } /*********************************************************************** * !Function: CalcCRC32OfFile * !Descr: calculates the CRC32 of the file. ***********************************************************************/ static uint32_t CalcCRC32OfFile(const char *szFileName) { char acBuffer[IO_BLOCK_SIZE]; int iFd = -1; uint32_t uiCRC32 = 0; iFd = open(szFileName, O_RDONLY); if (-1 == iFd) systemError("%s", szFileName); do { int iBytesRead = read(iFd, acBuffer, IO_BLOCK_SIZE); if (!iBytesRead) break; else if (iBytesRead < 0) systemError("%s", szFileName); uiCRC32 = crc32(uiCRC32, acBuffer, iBytesRead); } while (1); CLOSE(iFd); return uiCRC32; } /*********************************************************************** * !Function: GetRootDevice * !Descr: determines the rootdevice from kernel command line root= ***********************************************************************/ static const char *GetRootDevice(void) { int iFd = -1; int iRead; /* arm command line is at max 1024 Bytes */ char szCmdLine[1024]; static char szRootDev[32] = ""; const char *szRootStart = NULL; if (*szRootDev) return szRootDev; iFd = open("/proc/cmdline", O_RDONLY); if (-1 == iFd) systemError("/proc/cmdline"); iRead = read(iFd, szCmdLine, sizeof(szCmdLine) - 2); if (-1 == iRead) systemError("read"); szCmdLine[iRead + 1] = 0; CLOSE(iFd); szRootStart = strstr(szCmdLine, "root="); if (NULL != szRootStart) { const char *szDev = szRootStart + 5; /* strlen( root=) */ const char *szDevEnd = szDev; int iLen; szDevEnd = strchr(szDev, ' '); iLen = ((NULL == szDevEnd) ? strlen(szDev) : (szDevEnd - szDev)); strncpy(szRootDev, szDev, MIN(sizeof(szRootDev), iLen)); } return szRootDev; }