2019-05-23 12:14:39 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
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 >
2005-04-17 02:20:36 +04:00
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/init.h>
# include <linux/vmalloc.h>
2018-11-11 00:14:13 +03:00
# include <linux/of.h>
2005-04-17 02:20:36 +04:00
# 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 {
2021-05-20 14:48:51 +03:00
unsigned char name [ 16 ] ; // Null terminated name
u32 flash_base ; // Address within FLASH of image
u32 mem_base ; // Address in memory where it executes
u32 size ; // Length of image
u32 entry_point ; // Execution entry point
u32 data_length ; // Length of actual data
unsigned char _pad [ 256 - ( 16 + 7 * sizeof ( u32 ) ) ] ;
u32 desc_cksum ; // Checksum over image descriptor
u32 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 ;
}
2018-11-11 00:14:13 +03:00
static void parse_redboot_of ( struct mtd_info * master )
{
struct device_node * np ;
2021-05-20 14:48:50 +03:00
struct device_node * npart ;
2018-11-11 00:14:13 +03:00
u32 dirblock ;
int ret ;
np = mtd_get_of_node ( master ) ;
if ( ! np )
return ;
2021-05-20 14:48:50 +03:00
npart = of_get_child_by_name ( np , " partitions " ) ;
if ( ! npart )
return ;
ret = of_property_read_u32 ( npart , " fis-index-block " , & dirblock ) ;
2018-11-11 00:14:13 +03:00
if ( ret )
return ;
/*
* Assign the block found in the device tree to the local
* directory block pointer .
*/
directory = dirblock ;
}
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
2018-11-11 00:14:13 +03:00
parse_redboot_of ( master ) ;
2021-05-20 14:48:51 +03:00
if ( directory < 0 ) {
2007-10-29 04:57:02 +03:00
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 ) {
2021-05-20 14:48:51 +03:00
nogood :
pr_notice ( " Failed to find a non-bad block to check for RedBoot partition table \n " ) ;
2007-10-29 04:57:02 +03:00
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 ;
2021-05-20 14:48:51 +03:00
pr_notice ( " Searching for RedBoot partition table in %s at offset 0x%lx \n " ,
master - > name , offset ) ;
2005-04-17 02:20:36 +04:00
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 */
2021-05-20 14:48:51 +03:00
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 ) {
2021-05-20 14:48:51 +03:00
if ( buf [ j ] . name [ 1 ] = = 0xff ) {
2006-12-06 04:41:15 +03:00
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 */
2021-05-20 14:48:51 +03:00
pr_notice ( " No RedBoot partition table detected in %s \n " ,
master - > name ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
2021-05-20 14:48:51 +03:00
namelen + = strlen ( buf [ i ] . name ) + 1 ;
2005-04-17 02:20:36 +04:00
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
2021-05-20 14:48:51 +03:00
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 ;
2021-05-20 14:48:51 +03:00
while ( * prev & & ( * prev ) - > img - > flash_base < new_fl - > img - > flash_base )
2005-04-17 02:20:36 +04:00
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
2021-05-20 14:48:51 +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
2021-05-20 14:48:51 +03:00
if ( nulllen > 0 )
2005-04-17 02:20:36 +04:00
strcpy ( nullname , nullstring ) ;
# endif
names = nullname + nulllen ;
2021-05-20 14:48:51 +03:00
i = 0 ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
if ( fl - > img - > flash_base ) {
2021-05-20 14:48:51 +03:00
parts [ 0 ] . name = nullname ;
parts [ 0 ] . size = fl - > img - > flash_base ;
parts [ 0 ] . offset = 0 ;
2005-04-17 02:20:36 +04:00
i + + ;
}
# endif
2021-05-20 14:48:51 +03:00
for ( ; i < nrparts ; i + + ) {
2005-04-17 02:20:36 +04:00
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 ) | |
2021-05-20 14:48:51 +03:00
! memcmp ( names , " RedBoot config " , 15 ) | |
! memcmp ( names , " FIS directory " , 14 ) ) {
2005-04-17 02:20:36 +04:00
parts [ i ] . mask_flags = MTD_WRITEABLE ;
}
# endif
2021-05-20 14:48:51 +03:00
names + = strlen ( names ) + 1 ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
2021-05-20 14:48:51 +03:00
if ( fl - > next & & fl - > img - > flash_base + fl - > img - > size + master - > erasesize < = fl - > next - > img - > flash_base ) {
2005-04-17 02:20:36 +04:00
i + + ;
2021-05-20 14:48:51 +03:00
parts [ i ] . offset = parts [ i - 1 ] . size + parts [ i - 1 ] . offset ;
2005-04-17 02:20:36 +04:00
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 ;
2021-05-20 14:48:51 +03:00
2005-04-17 02:20:36 +04:00
fl = fl - > next ;
kfree ( old ) ;
}
vfree ( buf ) ;
return ret ;
}
2018-11-11 00:14:13 +03:00
static const struct of_device_id mtd_parser_redboot_of_match_table [ ] = {
{ . compatible = " redboot-fis " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , mtd_parser_redboot_of_match_table ) ;
2005-04-17 02:20:36 +04:00
static struct mtd_part_parser redboot_parser = {
. parse_fn = parse_redboot_partitions ,
. name = " RedBoot " ,
2018-11-11 00:14:13 +03:00
. of_match_table = mtd_parser_redboot_of_match_table ,
2005-04-17 02:20:36 +04:00
} ;
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 " ) ;