/* * docfdisk.c: Modify INFTL partition tables * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define PROGRAM_NAME "docfdisk" #define _XOPEN_SOURCE 500 /* for pread/pwrite */ #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <time.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <sys/mount.h> #include <errno.h> #include <string.h> #include <asm/types.h> #include <mtd/mtd-user.h> #include <mtd/inftl-user.h> #include <mtd_swab.h> unsigned char *buf; mtd_info_t meminfo; erase_info_t erase; int fd; struct INFTLMediaHeader *mh; #define MAXSCAN 10 void show_header(int mhoffs) { int i, unitsize, numunits, bmbits, numpart; int start, end, num, nextunit; unsigned int flags; struct INFTLPartition *ip; bmbits = le32_to_cpu(mh->BlockMultiplierBits); printf(" bootRecordID = %s\n" " NoOfBootImageBlocks = %d\n" " NoOfBinaryPartitions = %d\n" " NoOfBDTLPartitions = %d\n" " BlockMultiplierBits = %d\n" " FormatFlags = %d\n" " OsakVersion = %d.%d.%d.%d\n" " PercentUsed = %d\n", mh->bootRecordID, le32_to_cpu(mh->NoOfBootImageBlocks), le32_to_cpu(mh->NoOfBinaryPartitions), le32_to_cpu(mh->NoOfBDTLPartitions), bmbits, le32_to_cpu(mh->FormatFlags), ((unsigned char *) &mh->OsakVersion)[0] & 0xf, ((unsigned char *) &mh->OsakVersion)[1] & 0xf, ((unsigned char *) &mh->OsakVersion)[2] & 0xf, ((unsigned char *) &mh->OsakVersion)[3] & 0xf, le32_to_cpu(mh->PercentUsed)); numpart = le32_to_cpu(mh->NoOfBinaryPartitions) + le32_to_cpu(mh->NoOfBDTLPartitions); unitsize = meminfo.erasesize >> bmbits; numunits = meminfo.size / unitsize; nextunit = mhoffs / unitsize; nextunit++; printf("Unitsize is %d bytes. Device has %d units.\n", unitsize, numunits); if (numunits > 32768) { printf("WARNING: More than 32768 units! Unexpectedly small BlockMultiplierBits.\n"); } if (bmbits && (numunits <= 16384)) { printf("NOTICE: Unexpectedly large BlockMultiplierBits.\n"); } for (i = 0; i < 4; i++) { ip = &(mh->Partitions[i]); flags = le32_to_cpu(ip->flags); start = le32_to_cpu(ip->firstUnit); end = le32_to_cpu(ip->lastUnit); num = le32_to_cpu(ip->virtualUnits); if (start < nextunit) { printf("ERROR: Overlapping or misordered partitions!\n"); } if (start > nextunit) { printf(" Unpartitioned space: %d bytes\n" " virtualUnits = %d\n" " firstUnit = %d\n" " lastUnit = %d\n", (start - nextunit) * unitsize, start - nextunit, nextunit, start - 1); } if (flags & INFTL_BINARY) printf(" Partition %d (BDK):", i+1); else printf(" Partition %d (BDTL):", i+1); printf(" %d bytes\n" " virtualUnits = %d\n" " firstUnit = %d\n" " lastUnit = %d\n" " flags = 0x%x\n" " spareUnits = %d\n", num * unitsize, num, start, end, le32_to_cpu(ip->flags), le32_to_cpu(ip->spareUnits)); if (num > (1 + end - start)) { printf("ERROR: virtualUnits not consistent with first/lastUnit!\n"); } end++; if (end > nextunit) nextunit = end; if (flags & INFTL_LAST) break; } if (i >= 4) { printf("Odd. Last partition was not marked with INFTL_LAST.\n"); i--; } if ((i+1) != numpart) { printf("ERROR: Number of partitions != (NoOfBinaryPartitions + NoOfBDTLPartitions)\n"); } if (nextunit > numunits) { printf("ERROR: Partitions appear to extend beyond end of device!\n"); } if (nextunit < numunits) { printf(" Unpartitioned space: %d bytes\n" " virtualUnits = %d\n" " firstUnit = %d\n" " lastUnit = %d\n", (numunits - nextunit) * unitsize, numunits - nextunit, nextunit, numunits - 1); } } int main(int argc, char **argv) { int ret, i, mhblock, unitsize, block; unsigned int nblocks[4], npart; unsigned int totblocks; struct INFTLPartition *ip; unsigned char *oobbuf; struct mtd_oob_buf oob; char line[20]; int mhoffs; struct INFTLMediaHeader *mh2; if (argc < 2) { printf( "Usage: %s <mtddevice> [<size1> [<size2> [<size3> [<size4]]]]\n" " Sizes are in device units (run with no sizes to show unitsize and current\n" " partitions). Last size = 0 means go to end of device.\n", PROGRAM_NAME); return 1; } npart = argc - 2; if (npart > 4) { printf("Max 4 partitions allowed.\n"); return 1; } for (i = 0; i < npart; i++) { nblocks[i] = strtoul(argv[2+i], NULL, 0); if (i && !nblocks[i-1]) { printf("No sizes allowed after 0\n"); return 1; } } // Open and size the device if ((fd = open(argv[1], O_RDWR)) < 0) { perror("Open flash device"); return 1; } if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { perror("ioctl(MEMGETINFO)"); return 1; } printf("Device size is %d bytes. Erasesize is %d bytes.\n", meminfo.size, meminfo.erasesize); buf = malloc(meminfo.erasesize); oobbuf = malloc((meminfo.erasesize / meminfo.writesize) * meminfo.oobsize); if (!buf || !oobbuf) { printf("Can't malloc block buffer\n"); return 1; } oob.length = meminfo.oobsize; mh = (struct INFTLMediaHeader *) buf; for (mhblock = 0; mhblock < MAXSCAN; mhblock++) { if ((ret = pread(fd, buf, meminfo.erasesize, mhblock * meminfo.erasesize)) < 0) { if (errno == EBADMSG) { printf("ECC error at eraseblock %d\n", mhblock); continue; } perror("Read eraseblock"); return 1; } if (ret != meminfo.erasesize) { printf("Short read!\n"); return 1; } if (!strcmp("BNAND", mh->bootRecordID)) break; } if (mhblock >= MAXSCAN) { printf("Unable to find INFTL Media Header\n"); return 1; } printf("Found INFTL Media Header at block %d:\n", mhblock); mhoffs = mhblock * meminfo.erasesize; oob.ptr = oobbuf; oob.start = mhoffs; for (i = 0; i < meminfo.erasesize; i += meminfo.writesize) { if (ioctl(fd, MEMREADOOB, &oob)) { perror("ioctl(MEMREADOOB)"); return 1; } oob.start += meminfo.writesize; oob.ptr += meminfo.oobsize; } show_header(mhoffs); if (!npart) return 0; printf("\n-------------------------------------------------------------------------\n"); unitsize = meminfo.erasesize >> le32_to_cpu(mh->BlockMultiplierBits); totblocks = meminfo.size / unitsize; block = mhoffs / unitsize; block++; mh->NoOfBDTLPartitions = 0; mh->NoOfBinaryPartitions = npart; for (i = 0; i < npart; i++) { ip = &(mh->Partitions[i]); ip->firstUnit = cpu_to_le32(block); if (!nblocks[i]) nblocks[i] = totblocks - block; ip->virtualUnits = cpu_to_le32(nblocks[i]); block += nblocks[i]; ip->lastUnit = cpu_to_le32(block-1); ip->spareUnits = 0; ip->flags = cpu_to_le32(INFTL_BINARY); } if (block > totblocks) { printf("Requested partitions extend beyond end of device.\n"); return 1; } ip->flags = cpu_to_le32(INFTL_BINARY | INFTL_LAST); /* update the spare as well */ mh2 = (struct INFTLMediaHeader *) (buf + 4096); memcpy((void *) mh2, (void *) mh, sizeof(struct INFTLMediaHeader)); printf("\nProposed new Media Header:\n"); show_header(mhoffs); printf("\nReady to update device. Type 'yes' to proceed, anything else to abort: "); fgets(line, sizeof(line), stdin); if (strcmp("yes\n", line)) return 0; printf("Updating MediaHeader...\n"); erase.start = mhoffs; erase.length = meminfo.erasesize; if (ioctl(fd, MEMERASE, &erase)) { perror("ioctl(MEMERASE)"); printf("Your MediaHeader may be hosed. UHOH!\n"); return 1; } oob.ptr = oobbuf; oob.start = mhoffs; for (i = 0; i < meminfo.erasesize; i += meminfo.writesize) { memset(oob.ptr, 0xff, 6); // clear ECC. if (ioctl(fd, MEMWRITEOOB, &oob)) { perror("ioctl(MEMWRITEOOB)"); printf("Your MediaHeader may be hosed. UHOH!\n"); return 1; } if ((ret = pwrite(fd, buf, meminfo.writesize, oob.start)) < 0) { perror("Write page"); printf("Your MediaHeader may be hosed. UHOH!\n"); return 1; } if (ret != meminfo.writesize) { printf("Short write!\n"); printf("Your MediaHeader may be hosed. UHOH!\n"); return 1; } oob.start += meminfo.writesize; oob.ptr += meminfo.oobsize; buf += meminfo.writesize; } printf("Success. REBOOT or unload the diskonchip module to update partitions!\n"); return 0; }