X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=drivers%2Fmtd%2Fredboot.c;h=a61351f88ec046d894bf07bc042907e2a22bb364;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=0212a7df84c4d2f091100590bd87a411804e7a76;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 0212a7df8..a61351f88 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -1,5 +1,5 @@ /* - * $Id: redboot.c,v 1.15 2004/08/10 07:55:16 dwmw2 Exp $ + * $Id: redboot.c,v 1.21 2006/03/30 18:34:37 bjd Exp $ * * Parse RedBoot-style Flash Image System (FIS) tables and * produce a Linux partition array to match. @@ -15,14 +15,14 @@ struct fis_image_desc { unsigned char name[16]; // Null terminated name - unsigned long flash_base; // Address within FLASH of image - unsigned long mem_base; // Address in memory where it executes - unsigned long size; // Length of image - unsigned long entry_point; // Execution entry point - unsigned long data_length; // Length of actual data - unsigned char _pad[256-(16+7*sizeof(unsigned long))]; - unsigned long desc_cksum; // Checksum over image descriptor - unsigned long file_cksum; // Checksum over image data + uint32_t flash_base; // Address within FLASH of image + uint32_t mem_base; // Address in memory where it executes + uint32_t size; // Length of image + uint32_t entry_point; // Execution entry point + uint32_t data_length; // Length of actual data + unsigned char _pad[256-(16+7*sizeof(uint32_t))]; + uint32_t desc_cksum; // Checksum over image descriptor + uint32_t file_cksum; // Checksum over image data }; struct fis_list { @@ -30,13 +30,16 @@ struct fis_list { struct fis_list *next; }; +static int directory = CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK; +module_param(directory, int, 0); + static inline int redboot_checksum(struct fis_image_desc *img) { /* RedBoot doesn't actually write the desc_cksum field yet AFAICT */ return 1; } -static int parse_redboot_partitions(struct mtd_info *master, +static int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts, unsigned long fis_origin) { @@ -50,6 +53,8 @@ static int parse_redboot_partitions(struct mtd_info *master, char *nullname; int namelen = 0; int nulllen = 0; + int numslots; + unsigned long offset; #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED static char nullstring[] = "unallocated"; #endif @@ -59,8 +64,15 @@ static int parse_redboot_partitions(struct mtd_info *master, if (!buf) return -ENOMEM; - /* Read the start of the last erase block */ - ret = master->read(master, master->size - master->erasesize, + if ( directory < 0 ) + offset = master->size + directory*master->erasesize; + else + offset = directory*master->erasesize; + + printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n", + master->name, offset); + + ret = master->read(master, offset, master->erasesize, &retlen, (void *)buf); if (ret) @@ -71,12 +83,62 @@ static int parse_redboot_partitions(struct mtd_info *master, goto out; } - /* RedBoot image could appear in any of the first three slots */ - for (i = 0; i < 3; i++) { - if (!memcmp(buf[i].name, "RedBoot", 8)) + numslots = (master->erasesize / sizeof(struct fis_image_desc)); + for (i = 0; i < numslots; i++) { + if (!memcmp(buf[i].name, "FIS directory", 14)) { + /* This is apparently the FIS directory entry for the + * FIS directory itself. The FIS directory size is + * one erase block; if the buf[i].size field is + * swab32(erasesize) then we know we are looking at + * a byte swapped FIS directory - swap all the entries! + * (NOTE: this is 'size' not 'data_length'; size is + * the full size of the entry.) + */ + + /* RedBoot can combine the FIS directory and + config partitions into a single eraseblock; + we assume wrong-endian if either the swapped + 'size' matches the eraseblock size precisely, + or if the swapped size actually fits in an + eraseblock while the unswapped size doesn't. */ + if (swab32(buf[i].size) == master->erasesize || + (buf[i].size > master->erasesize + && swab32(buf[i].size) < master->erasesize)) { + int j; + /* Update numslots based on actual FIS directory size */ + numslots = swab32(buf[i].size) / sizeof (struct fis_image_desc); + for (j = 0; j < numslots; ++j) { + + /* A single 0xff denotes a deleted entry. + * Two of them in a row is the end of the table. + */ + if (buf[j].name[0] == 0xff) { + if (buf[j].name[1] == 0xff) { + break; + } else { + continue; + } + } + + /* The unsigned long fields were written with the + * wrong byte sex, name and pad have no byte sex. + */ + swab32s(&buf[j].flash_base); + swab32s(&buf[j].mem_base); + swab32s(&buf[j].size); + swab32s(&buf[j].entry_point); + swab32s(&buf[j].data_length); + swab32s(&buf[j].desc_cksum); + swab32s(&buf[j].file_cksum); + } + } else if (buf[i].size < master->erasesize) { + /* Update numslots based on actual FIS directory size */ + numslots = buf[i].size / sizeof(struct fis_image_desc); + } break; + } } - if (i == 3) { + if (i == numslots) { /* Didn't find it */ printk(KERN_NOTICE "No RedBoot partition table detected in %s\n", master->name); @@ -84,11 +146,16 @@ static int parse_redboot_partitions(struct mtd_info *master, goto out; } - for (i = 0; i < master->erasesize / sizeof(struct fis_image_desc); i++) { + for (i = 0; i < numslots; i++) { struct fis_list *new_fl, **prev; - if (buf[i].name[0] == 0xff) - break; + if (buf[i].name[0] == 0xff) { + if (buf[i].name[1] == 0xff) { + break; + } else { + continue; + } + } if (!redboot_checksum(&buf[i])) break; @@ -129,15 +196,13 @@ static int parse_redboot_partitions(struct mtd_info *master, } } #endif - parts = kmalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL); + parts = kzalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL); if (!parts) { ret = -ENOMEM; goto out; } - memset(parts, 0, sizeof(*parts)*nrparts + nulllen + namelen); - nullname = (char *)&parts[nrparts]; #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED if (nulllen > 0) {