2005-04-17 02:20:36 +04:00
/*
* Parse RedBoot - style Flash Image System ( FIS ) tables and
* produce a Linux partition array to match .
2010-08-08 23:58:20 +04:00
*
* Copyright © 2001 Red Hat UK Limited
* Copyright © 2001 - 2010 David Woodhouse < dwmw2 @ infradead . org >
*
* 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 . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*
2005-04-17 02:20:36 +04:00
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/init.h>
# include <linux/vmalloc.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/partitions.h>
2011-07-03 23:17:31 +04:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
struct fis_image_desc {
unsigned char name [ 16 ] ; // Null terminated name
2006-05-16 20:31:15 +04:00
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
2005-04-17 02:20:36 +04:00
} ;
struct fis_list {
struct fis_image_desc * img ;
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 ;
}
2005-11-07 14:15:26 +03:00
static int parse_redboot_partitions ( struct mtd_info * master ,
2015-12-05 02:25:14 +03:00
const struct mtd_partition * * pparts ,
2011-06-10 18:18:28 +04:00
struct mtd_part_parser_data * data )
2005-04-17 02:20:36 +04:00
{
int nrparts = 0 ;
struct fis_image_desc * buf ;
struct mtd_partition * parts ;
struct fis_list * fl = NULL , * tmp_fl ;
int ret , i ;
size_t retlen ;
char * names ;
char * nullname ;
int namelen = 0 ;
int nulllen = 0 ;
int numslots ;
unsigned long offset ;
# ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
static char nullstring [ ] = " unallocated " ;
# endif
2007-10-29 04:57:02 +03:00
if ( directory < 0 ) {
offset = master - > size + directory * master - > erasesize ;
2012-02-03 13:37:47 +04:00
while ( mtd_block_isbad ( master , offset ) ) {
2007-10-29 04:57:02 +03:00
if ( ! offset ) {
nogood :
printk ( KERN_NOTICE " Failed to find a non-bad block to check for RedBoot partition table \n " ) ;
return - EIO ;
}
offset - = master - > erasesize ;
}
} else {
offset = directory * master - > erasesize ;
2012-02-03 13:37:47 +04:00
while ( mtd_block_isbad ( master , offset ) ) {
2007-10-29 04:57:02 +03:00
offset + = master - > erasesize ;
if ( offset = = master - > size )
goto nogood ;
}
}
2005-04-17 02:20:36 +04:00
buf = vmalloc ( master - > erasesize ) ;
if ( ! buf )
return - ENOMEM ;
printk ( KERN_NOTICE " Searching for RedBoot partition table in %s at offset 0x%lx \n " ,
master - > name , offset ) ;
2011-12-23 19:30:16 +04:00
ret = mtd_read ( master , offset , master - > erasesize , & retlen ,
( void * ) buf ) ;
2005-04-17 02:20:36 +04:00
if ( ret )
goto out ;
if ( retlen ! = master - > erasesize ) {
ret = - EIO ;
goto out ;
}
numslots = ( master - > erasesize / sizeof ( struct fis_image_desc ) ) ;
for ( i = 0 ; i < numslots ; i + + ) {
2006-03-01 03:59:08 +03:00
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
2006-03-02 13:54:29 +03:00
* one erase block ; if the buf [ i ] . size field is
2006-03-01 03:59:08 +03:00
* swab32 ( erasesize ) then we know we are looking at
* a byte swapped FIS directory - swap all the entries !
2006-03-02 13:54:29 +03:00
* ( NOTE : this is ' size ' not ' data_length ' ; size is
2006-03-01 03:59:08 +03:00
* the full size of the entry . )
*/
2007-02-10 12:58:31 +03:00
/* 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 ) ) {
2006-03-01 03:59:08 +03:00
int j ;
2007-02-10 12:26:48 +03:00
/* Update numslots based on actual FIS directory size */
numslots = swab32 ( buf [ i ] . size ) / sizeof ( struct fis_image_desc ) ;
2006-12-06 04:41:15 +03:00
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 ;
}
}
2006-03-01 03:59:08 +03:00
/* The unsigned long fields were written with the
* wrong byte sex , name and pad have no byte sex .
*/
2006-03-02 13:54:29 +03:00
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 ) ;
2006-03-01 03:59:08 +03:00
}
2007-02-10 12:58:31 +03:00
} else if ( buf [ i ] . size < master - > erasesize ) {
2007-02-10 12:26:48 +03:00
/* Update numslots based on actual FIS directory size */
numslots = buf [ i ] . size / sizeof ( struct fis_image_desc ) ;
2006-03-01 03:59:08 +03:00
}
2005-04-17 02:20:36 +04:00
break ;
2006-03-01 03:59:08 +03:00
}
2005-04-17 02:20:36 +04:00
}
if ( i = = numslots ) {
/* Didn't find it */
printk ( KERN_NOTICE " No RedBoot partition table detected in %s \n " ,
master - > name ) ;
ret = 0 ;
goto out ;
}
for ( i = 0 ; i < numslots ; i + + ) {
struct fis_list * new_fl , * * prev ;
2006-12-06 04:41:15 +03:00
if ( buf [ i ] . name [ 0 ] = = 0xff ) {
if ( buf [ i ] . name [ 1 ] = = 0xff ) {
break ;
} else {
continue ;
}
}
2005-04-17 02:20:36 +04:00
if ( ! redboot_checksum ( & buf [ i ] ) )
break ;
new_fl = kmalloc ( sizeof ( struct fis_list ) , GFP_KERNEL ) ;
namelen + = strlen ( buf [ i ] . name ) + 1 ;
if ( ! new_fl ) {
ret = - ENOMEM ;
goto out ;
}
new_fl - > img = & buf [ i ] ;
2011-06-10 18:18:28 +04:00
if ( data & & data - > origin )
buf [ i ] . flash_base - = data - > origin ;
else
buf [ i ] . flash_base & = master - > size - 1 ;
2005-04-17 02:20:36 +04:00
/* I'm sure the JFFS2 code has done me permanent damage.
* I now think the following is _normal_
*/
prev = & fl ;
while ( * prev & & ( * prev ) - > img - > flash_base < new_fl - > img - > flash_base )
prev = & ( * prev ) - > next ;
new_fl - > next = * prev ;
* prev = new_fl ;
nrparts + + ;
}
# ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
if ( fl - > img - > flash_base ) {
nrparts + + ;
nulllen = sizeof ( nullstring ) ;
}
for ( tmp_fl = fl ; tmp_fl - > next ; tmp_fl = tmp_fl - > next ) {
if ( tmp_fl - > img - > flash_base + tmp_fl - > img - > size + master - > erasesize < = tmp_fl - > next - > img - > flash_base ) {
nrparts + + ;
nulllen = sizeof ( nullstring ) ;
}
}
# endif
2006-11-15 22:10:29 +03:00
parts = kzalloc ( sizeof ( * parts ) * nrparts + nulllen + namelen , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! parts ) {
ret = - ENOMEM ;
goto out ;
}
nullname = ( char * ) & parts [ nrparts ] ;
# ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
if ( nulllen > 0 ) {
strcpy ( nullname , nullstring ) ;
}
# endif
names = nullname + nulllen ;
i = 0 ;
# ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
if ( fl - > img - > flash_base ) {
parts [ 0 ] . name = nullname ;
parts [ 0 ] . size = fl - > img - > flash_base ;
parts [ 0 ] . offset = 0 ;
i + + ;
}
# endif
for ( ; i < nrparts ; i + + ) {
parts [ i ] . size = fl - > img - > size ;
parts [ i ] . offset = fl - > img - > flash_base ;
parts [ i ] . name = names ;
strcpy ( names , fl - > img - > name ) ;
# ifdef CONFIG_MTD_REDBOOT_PARTS_READONLY
if ( ! memcmp ( names , " RedBoot " , 8 ) | |
! memcmp ( names , " RedBoot config " , 15 ) | |
! memcmp ( names , " FIS directory " , 14 ) ) {
parts [ i ] . mask_flags = MTD_WRITEABLE ;
}
# endif
names + = strlen ( names ) + 1 ;
# ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
if ( fl - > next & & fl - > img - > flash_base + fl - > img - > size + master - > erasesize < = fl - > next - > img - > flash_base ) {
i + + ;
parts [ i ] . offset = parts [ i - 1 ] . size + parts [ i - 1 ] . offset ;
parts [ i ] . size = fl - > next - > img - > flash_base - parts [ i ] . offset ;
parts [ i ] . name = nullname ;
}
# endif
tmp_fl = fl ;
fl = fl - > next ;
kfree ( tmp_fl ) ;
}
ret = nrparts ;
* pparts = parts ;
out :
while ( fl ) {
struct fis_list * old = fl ;
fl = fl - > next ;
kfree ( old ) ;
}
vfree ( buf ) ;
return ret ;
}
static struct mtd_part_parser redboot_parser = {
. parse_fn = parse_redboot_partitions ,
. name = " RedBoot " ,
} ;
2015-11-12 06:13:30 +03:00
module_mtd_part_parser ( redboot_parser ) ;
2005-04-17 02:20:36 +04:00
2011-10-14 18:33:20 +04:00
/* mtd parsers will request the module by parser name */
MODULE_ALIAS ( " RedBoot " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
2008-06-06 09:46:18 +04:00
MODULE_AUTHOR ( " David Woodhouse <dwmw2@infradead.org> " ) ;
2005-04-17 02:20:36 +04:00
MODULE_DESCRIPTION ( " Parsing code for RedBoot Flash Image System (FIS) tables " ) ;