diff options
Diffstat (limited to 'nftl_format.c')
-rw-r--r-- | nftl_format.c | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/nftl_format.c b/nftl_format.c new file mode 100644 index 0000000..a04411a --- /dev/null +++ b/nftl_format.c @@ -0,0 +1,442 @@ +/* + * nftl_format.c: Creating a NFTL/INFTL partition on an MTD device + * + * + * $Id: nftl_format.c,v 1.24 2005/11/07 11:15:13 gleixner Exp $ + * + * 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 + * + * ToDo: + * 1. UnitSizeFactor != 0xFF cases + * 2. test, test, and test !!! + */ + +#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/nftl-user.h> +#include <mtd/inftl-user.h> + +#define swab16(x) \ + ((__u16)( \ + (((__u16)(x) & (__u16)0x00ffU) << 8) | \ + (((__u16)(x) & (__u16)0xff00U) >> 8) )) +#define swab32(x) \ + ((__u32)( \ + (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \ + (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \ + (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \ + (((__u32)(x) & (__u32)0xff000000UL) >> 24) )) + +#if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_le16(x) ({ __u16 _x = x; swab16(_x); }) +#define cpu_to_le32(x) ({ __u32 _x = x; swab32(_x); }) +#else +#define cpu_to_le16(x) (x) +#define cpu_to_le32(x) (x) +#endif +#define le32_to_cpu(x) cpu_to_le32(x) +#define le16_to_cpu(x) cpu_to_le16(x) + +unsigned char BadUnitTable[MAX_ERASE_ZONES]; +unsigned char *readbuf; +unsigned char *writebuf[4]; + +mtd_info_t meminfo; +erase_info_t erase; +int fd; +struct NFTLMediaHeader *NFTLhdr; +struct INFTLMediaHeader *INFTLhdr; + +static int do_oobcheck = 1; +static int do_rwecheck = 1; + +static unsigned char check_block_1(unsigned long block) +{ + unsigned char oobbuf[16]; + struct mtd_oob_buf oob = { 0, 16, oobbuf }; + + oob.start = block * meminfo.erasesize; + if (ioctl(fd, MEMREADOOB, &oob)) + return ZONE_BAD_ORIGINAL; + + if(oobbuf[5] == 0) + return ZONE_BAD_ORIGINAL; + + oob.start = block * meminfo.erasesize + 512 /* FIXME */; + if (ioctl(fd, MEMREADOOB, &oob)) + return ZONE_BAD_ORIGINAL; + + if(oobbuf[5] == 0) + return ZONE_BAD_ORIGINAL; + + return ZONE_GOOD; +} + +static unsigned char check_block_2(unsigned long block) +{ + unsigned long ofs = block * meminfo.erasesize; + unsigned long blockofs; + + /* Erase test */ + erase.start = ofs; + + for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) { + pread(fd, readbuf, 512, ofs + blockofs); + if (memcmp(readbuf, writebuf[0], 512)) { + /* Block wasn't 0xff after erase */ + printf(": Block not 0xff after erase\n"); + return ZONE_BAD_ORIGINAL; + } + + pwrite(fd, writebuf[1], 512, blockofs + ofs); + pread(fd, readbuf, 512, blockofs + ofs); + if (memcmp(readbuf, writebuf[1], 512)) { + printf(": Block not zero after clearing\n"); + return ZONE_BAD_ORIGINAL; + } + } + + /* Write test */ + if (ioctl(fd, MEMERASE, &erase) != 0) { + printf(": Second erase failed (%s)\n", strerror(errno)); + return ZONE_BAD_ORIGINAL; + } + for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) { + pwrite(fd, writebuf[2], 512, blockofs + ofs); + pread(fd, readbuf, 512, blockofs + ofs); + if (memcmp(readbuf, writebuf[2], 512)) { + printf(": Block not 0x5a after writing\n"); + return ZONE_BAD_ORIGINAL; + } + } + + if (ioctl(fd, MEMERASE, &erase) != 0) { + printf(": Third erase failed (%s)\n", strerror(errno)); + return ZONE_BAD_ORIGINAL; + } + for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) { + pwrite(fd, writebuf[3], 512, blockofs + ofs); + pread(fd, readbuf, 512, blockofs + ofs); + if (memcmp(readbuf, writebuf[3], 512)) { + printf(": Block not 0xa5 after writing\n"); + return ZONE_BAD_ORIGINAL; + } + } + if (ioctl(fd, MEMERASE, &erase) != 0) { + printf(": Fourth erase failed (%s)\n", strerror(errno)); + return ZONE_BAD_ORIGINAL; + } + return ZONE_GOOD; +} + +static unsigned char erase_block(unsigned long block) +{ + unsigned char status; + int ret; + + status = (do_oobcheck) ? check_block_1(block) : ZONE_GOOD; + erase.start = block * meminfo.erasesize; + + if (status != ZONE_GOOD) { + printf("\rSkipping bad zone (factory marked) #%ld @ 0x%x\n", block, erase.start); + fflush(stdout); + return status; + } + + printf("\r\t Erasing Zone #%ld @ 0x%x", block, erase.start); + fflush(stdout); + + if ((ret=ioctl(fd, MEMERASE, &erase)) != 0) { + printf(": Erase failed (%s)\n", strerror(errno)); + return ZONE_BAD_ORIGINAL; + } + + if (do_rwecheck) { + printf("\r\tChecking Zone #%ld @ 0x%x", block, erase.start); + fflush(stdout); + status = check_block_2(block); + if (status != ZONE_GOOD) { + printf("\rSkipping bad zone (RWE test failed) #%ld @ 0x%x\n", block, erase.start); + fflush(stdout); + } + } + return status; +} + +static int checkbbt(void) +{ + unsigned char bbt[512]; + unsigned char bits; + int i, addr; + + if (pread(fd, bbt, 512, 0x800) < 0) { + printf("nftl_format: failed to read BBT, errno=%d\n", errno); + return (-1); + } + + + for (i = 0; (i < 512); i++) { + addr = i / 4; + bits = 0x3 << ((i % 4) * 2); + if ((bbt[addr] & bits) == 0) { + BadUnitTable[i] = ZONE_BAD_ORIGINAL; + } + } + + return (0); +} + +void usage(int rc) +{ + fprintf(stderr, "Usage: nftl_format [-ib] <mtddevice> [<start offset> [<size>]]\n"); + exit(rc); +} + +int main(int argc, char **argv) +{ + unsigned long startofs = 0, part_size = 0; + unsigned long ezones = 0, ezone = 0, bad_zones = 0; + unsigned char unit_factor = 0xFF; + long MediaUnit1 = -1, MediaUnit2 = -1; + long MediaUnitOff1 = 0, MediaUnitOff2 = 0; + unsigned char oobbuf[16]; + struct mtd_oob_buf oob = {0, 16, oobbuf}; + char *mtddevice, *nftl; + int c, do_inftl = 0, do_bbt = 0; + + + printf("$Id: nftl_format.c,v 1.24 2005/11/07 11:15:13 gleixner Exp $\n"); + + if (argc < 2) + usage(1); + + nftl = "NFTL"; + + while ((c = getopt(argc, argv, "?hib")) > 0) { + switch (c) { + case 'i': + nftl = "INFTL"; + do_inftl = 1; + break; + case 'b': + do_bbt = 1; + break; + case 'h': + case '?': + usage(0); + break; + default: + usage(1); + break; + } + } + + mtddevice = argv[optind++]; + if (argc > optind) { + startofs = strtoul(argv[optind++], NULL, 0); + } + if (argc > optind) { + part_size = strtoul(argv[optind++], NULL, 0); + } + + // Open and size the device + if ((fd = open(mtddevice, O_RDWR)) < 0) { + perror("Open flash device"); + return 1; + } + + if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { + perror("ioctl(MEMGETINFO)"); + close(fd); + return 1; + } + + switch (meminfo.erasesize) { + case 0x1000: + case 0x2000: + case 0x4000: + case 0x8000: + break; + default: + printf("Unrecognized Erase size, 0x%x - I'm confused\n", + meminfo.erasesize); + close(fd); + return 1; + } + writebuf[0] = malloc(meminfo.erasesize * 5); + if (!writebuf[0]) { + printf("Malloc failed\n"); + close(fd); + return 1; + } + writebuf[1] = writebuf[0] + meminfo.erasesize; + writebuf[2] = writebuf[1] + meminfo.erasesize; + writebuf[3] = writebuf[2] + meminfo.erasesize; + readbuf = writebuf[3] + meminfo.erasesize; + memset(writebuf[0], 0xff, meminfo.erasesize); + memset(writebuf[1], 0x00, meminfo.erasesize); + memset(writebuf[2], 0x5a, meminfo.erasesize); + memset(writebuf[3], 0xa5, meminfo.erasesize); + memset(BadUnitTable, ZONE_GOOD, MAX_ERASE_ZONES); + + if (part_size == 0 || (part_size > meminfo.size - startofs)) + /* the user doest not or incorrectly specify NFTL partition size */ + part_size = meminfo.size - startofs; + + erase.length = meminfo.erasesize; + ezones = part_size / meminfo.erasesize; + + if (ezones > MAX_ERASE_ZONES) { + /* Ought to change the UnitSizeFactor. But later. */ + part_size = meminfo.erasesize * MAX_ERASE_ZONES; + ezones = MAX_ERASE_ZONES; + unit_factor = 0xFF; + } + + /* If using device BBT then parse that now */ + if (do_bbt) { + checkbbt(); + do_oobcheck = 0; + do_rwecheck = 0; + } + + /* Phase 1. Erasing and checking each erase zones in the NFTL partition. + N.B. Erase Zones not used by the NFTL partition are untouched and marked ZONE_GOOD */ + printf("Phase 1. Checking and erasing Erase Zones from 0x%08lx to 0x%08lx\n", + startofs, startofs + part_size); + for (ezone = startofs / meminfo.erasesize; + ezone < (ezones + startofs / meminfo.erasesize); ezone++) { + if (BadUnitTable[ezone] != ZONE_GOOD) + continue; + if ((BadUnitTable[ezone] = erase_block(ezone)) == ZONE_GOOD) { + if (MediaUnit1 == -1) { + MediaUnit1 = ezone; + } else if (MediaUnit2 == -1) { + MediaUnit2 = ezone; + } + } else { + bad_zones++; + } + } + printf("\n"); + + /* N.B. from dump of M-System original chips, NumEraseUnits counts the 2 Erase Unit used + by MediaHeader and the FirstPhysicalEUN starts from the MediaHeader */ + if (do_inftl) { + unsigned long maxzones, pezstart, pezend, numvunits; + + INFTLhdr = (struct INFTLMediaHeader *) (writebuf[0]); + strcpy(INFTLhdr->bootRecordID, "BNAND"); + INFTLhdr->NoOfBootImageBlocks = cpu_to_le32(0); + INFTLhdr->NoOfBinaryPartitions = cpu_to_le32(0); + INFTLhdr->NoOfBDTLPartitions = cpu_to_le32(1); + INFTLhdr->BlockMultiplierBits = cpu_to_le32(0); + INFTLhdr->FormatFlags = cpu_to_le32(0); + INFTLhdr->OsakVersion = cpu_to_le32(OSAK_VERSION); + INFTLhdr->PercentUsed = cpu_to_le32(PERCENTUSED); + /* + * Calculate number of virtual units we will have to work + * with. I am calculating out the known bad units here, not + * sure if that is what M-Systems do... + */ + MediaUnit2 = MediaUnit1; + MediaUnitOff2 = 4096; + maxzones = meminfo.size / meminfo.erasesize; + pezstart = startofs / meminfo.erasesize + 1; + pezend = startofs / meminfo.erasesize + ezones - 1; + numvunits = (ezones - 2) * PERCENTUSED / 100; + for (ezone = pezstart; ezone < maxzones; ezone++) { + if (BadUnitTable[ezone] != ZONE_GOOD) { + if (numvunits > 1) + numvunits--; + } + } + + INFTLhdr->Partitions[0].virtualUnits = cpu_to_le32(numvunits); + INFTLhdr->Partitions[0].firstUnit = cpu_to_le32(pezstart); + INFTLhdr->Partitions[0].lastUnit = cpu_to_le32(pezend); + INFTLhdr->Partitions[0].flags = cpu_to_le32(INFTL_BDTL); + INFTLhdr->Partitions[0].spareUnits = cpu_to_le32(0); + INFTLhdr->Partitions[0].Reserved0 = INFTLhdr->Partitions[0].firstUnit; + INFTLhdr->Partitions[0].Reserved1 = cpu_to_le32(0); + + } else { + + NFTLhdr = (struct NFTLMediaHeader *) (writebuf[0]); + strcpy(NFTLhdr->DataOrgID, "ANAND"); + NFTLhdr->NumEraseUnits = cpu_to_le16(part_size / meminfo.erasesize); + NFTLhdr->FirstPhysicalEUN = cpu_to_le16(MediaUnit1); + /* N.B. we reserve 2 more Erase Units for "folding" of Virtual Unit Chain */ + NFTLhdr->FormattedSize = cpu_to_le32(part_size - ( (5+bad_zones) * meminfo.erasesize)); + NFTLhdr->UnitSizeFactor = unit_factor; + } + + /* Phase 2. Writing NFTL Media Headers and Bad Unit Table */ + printf("Phase 2.a Writing %s Media Header and Bad Unit Table\n", nftl); + pwrite(fd, writebuf[0], 512, MediaUnit1 * meminfo.erasesize + MediaUnitOff1); + for (ezone = 0; ezone < (meminfo.size / meminfo.erasesize); ezone += 512) { + pwrite(fd, BadUnitTable + ezone, 512, + (MediaUnit1 * meminfo.erasesize) + 512 * (1 + ezone / 512)); + } + +#if 0 + printf(" MediaHeader contents:\n"); + printf(" NumEraseUnits: %d\n", le16_to_cpu(NFTLhdr->NumEraseUnits)); + printf(" FirstPhysicalEUN: %d\n", le16_to_cpu(NFTLhdr->FirstPhysicalEUN)); + printf(" FormattedSize: %d (%d sectors)\n", le32_to_cpu(NFTLhdr->FormattedSize), + le32_to_cpu(NFTLhdr->FormattedSize)/512); +#endif + printf("Phase 2.b Writing Spare %s Media Header and Spare Bad Unit Table\n", nftl); + pwrite(fd, writebuf[0], 512, MediaUnit2 * meminfo.erasesize + MediaUnitOff2); + for (ezone = 0; ezone < (meminfo.size / meminfo.erasesize); ezone += 512) { + pwrite(fd, BadUnitTable + ezone, 512, + (MediaUnit2 * meminfo.erasesize + MediaUnitOff2) + 512 * (1 + ezone / 512)); + } + + /* UCI #1 for newly erased Erase Unit */ + memset(oobbuf, 0xff, 16); + oobbuf[11] = oobbuf[10] = oobbuf[9] = 0; + oobbuf[8] = (do_inftl) ? 0x00 : 0x03; + oobbuf[12] = oobbuf[14] = 0x69; + oobbuf[13] = oobbuf[15] = 0x3c; + + /* N.B. The Media Header and Bad Erase Unit Table are considered as Free Erase Unit + by M-System i.e. their Virtual Unit Number == 0xFFFF in the Unit Control Information #0, + but their Block Status is BLOCK_USED (0x5555) in their Block Control Information */ + /* Phase 3. Writing Unit Control Information for each Erase Unit */ + printf("Phase 3. Writing Unit Control Information to each Erase Unit\n"); + for (ezone = MediaUnit1; ezone < (ezones + startofs / meminfo.erasesize); ezone++) { + /* write UCI #1 to each Erase Unit */ + if (BadUnitTable[ezone] != ZONE_GOOD) + continue; + oob.start = (ezone * meminfo.erasesize) + 512 + (do_inftl * 512); + if (ioctl(fd, MEMWRITEOOB, &oob)) + printf("MEMWRITEOOB at %lx: %s\n", (unsigned long)oob.start, strerror(errno)); + } + + exit(0); +} |