| /* |
| * 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 _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", |
| argv[0]); |
| 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; |
| } |