2019-05-23 11:14:39 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2011-12-05 16:08:08 +01:00
/*
* BCM63XX CFE image tag parser
*
* Copyright © 2006 - 2008 Florian Fainelli < florian @ openwrt . org >
* Mike Albon < malbon @ openwrt . org >
* Copyright © 2009 - 2010 Daniel Dickinson < openwrt @ cshore . neomailbox . net >
2013-03-23 14:07:49 +01:00
* Copyright © 2011 - 2013 Jonas Gorski < jonas . gorski @ gmail . com >
2011-12-05 16:08:08 +01:00
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2015-12-13 22:49:26 +00:00
# include <linux/bcm963xx_nvram.h>
2015-12-13 22:46:59 +00:00
# include <linux/bcm963xx_tag.h>
2011-12-17 13:58:18 +01:00
# include <linux/crc32.h>
2011-12-05 16:08:08 +01:00
# include <linux/module.h>
# include <linux/kernel.h>
2013-03-23 14:07:47 +01:00
# include <linux/sizes.h>
2011-12-05 16:08:08 +01:00
# include <linux/slab.h>
# include <linux/vmalloc.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/partitions.h>
2019-03-28 15:19:07 +01:00
# include <linux/of.h>
2011-12-05 16:08:08 +01:00
2015-12-13 22:49:26 +00:00
# define BCM963XX_CFE_BLOCK_SIZE SZ_64K /* always at least 64KiB */
2011-12-05 16:08:08 +01:00
2015-12-13 22:49:26 +00:00
# define BCM963XX_CFE_MAGIC_OFFSET 0x4e0
# define BCM963XX_CFE_VERSION_OFFSET 0x570
# define BCM963XX_NVRAM_OFFSET 0x580
2011-12-17 13:58:14 +01:00
2015-12-13 22:51:02 +00:00
/* Ensure strings read from flash structs are null terminated */
# define STR_NULL_TERMINATE(x) \
do { char * _str = ( x ) ; _str [ sizeof ( x ) - 1 ] = 0 ; } while ( 0 )
2011-12-05 16:08:08 +01:00
static int bcm63xx_detect_cfe ( struct mtd_info * master )
{
char buf [ 9 ] ;
int ret ;
size_t retlen ;
2011-12-23 17:30:16 +02:00
ret = mtd_read ( master , BCM963XX_CFE_VERSION_OFFSET , 5 , & retlen ,
( void * ) buf ) ;
2011-12-17 13:58:14 +01:00
buf [ retlen ] = 0 ;
if ( ret )
return ret ;
if ( strncmp ( " cfe-v " , buf , 5 ) = = 0 )
return 0 ;
/* very old CFE's do not have the cfe-v string, so check for magic */
2015-12-13 22:49:26 +00:00
ret = mtd_read ( master , BCM963XX_CFE_MAGIC_OFFSET , 8 , & retlen ,
2011-12-23 17:30:16 +02:00
( void * ) buf ) ;
2011-12-05 16:08:08 +01:00
buf [ retlen ] = 0 ;
2011-12-17 13:58:14 +01:00
return strncmp ( " CFE1CFE1 " , buf , 8 ) ;
2011-12-05 16:08:08 +01:00
}
2015-12-13 22:49:26 +00:00
static int bcm63xx_read_nvram ( struct mtd_info * master ,
struct bcm963xx_nvram * nvram )
{
u32 actual_crc , expected_crc ;
size_t retlen ;
int ret ;
/* extract nvram data */
ret = mtd_read ( master , BCM963XX_NVRAM_OFFSET , BCM963XX_NVRAM_V5_SIZE ,
& retlen , ( void * ) nvram ) ;
if ( ret )
return ret ;
ret = bcm963xx_nvram_checksum ( nvram , & expected_crc , & actual_crc ) ;
if ( ret )
pr_warn ( " nvram checksum failed, contents may be invalid (expected %08x, got %08x) \n " ,
expected_crc , actual_crc ) ;
if ( ! nvram - > psi_size )
nvram - > psi_size = BCM963XX_DEFAULT_PSI_SIZE ;
return 0 ;
}
2019-03-28 15:19:08 +01:00
static const char * const bcm63xx_cfe_part_types [ ] = {
" bcm963xx-imagetag " ,
NULL ,
} ;
2015-12-13 22:51:02 +00:00
2015-12-13 22:52:15 +00:00
static int bcm63xx_parse_cfe_nor_partitions ( struct mtd_info * master ,
const struct mtd_partition * * pparts , struct bcm963xx_nvram * nvram )
2011-12-05 16:08:08 +01:00
{
struct mtd_partition * parts ;
2019-03-28 15:19:08 +01:00
int nrparts = 3 , curpart = 0 ;
2011-12-19 11:36:04 +01:00
unsigned int cfelen , nvramlen ;
2012-11-12 10:52:49 +01:00
unsigned int cfe_erasesize ;
2011-12-05 16:08:08 +01:00
int i ;
2012-11-12 10:52:49 +01:00
cfe_erasesize = max_t ( uint32_t , master - > erasesize ,
2015-12-13 22:49:26 +00:00
BCM963XX_CFE_BLOCK_SIZE ) ;
2012-11-12 10:52:49 +01:00
cfelen = cfe_erasesize ;
2015-12-13 22:49:26 +00:00
nvramlen = nvram - > psi_size * SZ_1K ;
2013-03-23 14:07:49 +01:00
nvramlen = roundup ( nvramlen , cfe_erasesize ) ;
2011-12-19 11:36:04 +01:00
2011-12-05 16:08:08 +01:00
parts = kzalloc ( sizeof ( * parts ) * nrparts + 10 * nrparts , GFP_KERNEL ) ;
2019-03-28 15:19:08 +01:00
if ( ! parts )
return - ENOMEM ;
2011-12-05 16:08:08 +01:00
/* Start building partition list */
parts [ curpart ] . name = " CFE " ;
parts [ curpart ] . offset = 0 ;
2011-12-19 11:36:04 +01:00
parts [ curpart ] . size = cfelen ;
2011-12-05 16:08:08 +01:00
curpart + + ;
parts [ curpart ] . name = " nvram " ;
2011-12-19 11:36:04 +01:00
parts [ curpart ] . offset = master - > size - nvramlen ;
parts [ curpart ] . size = nvramlen ;
2012-11-12 10:52:50 +01:00
curpart + + ;
2011-12-05 16:08:08 +01:00
/* Global partition "linux" to make easy firmware upgrade */
parts [ curpart ] . name = " linux " ;
2011-12-17 13:58:16 +01:00
parts [ curpart ] . offset = cfelen ;
parts [ curpart ] . size = master - > size - cfelen - nvramlen ;
2019-03-28 15:19:08 +01:00
parts [ curpart ] . types = bcm63xx_cfe_part_types ;
2011-12-05 16:08:08 +01:00
for ( i = 0 ; i < nrparts ; i + + )
2012-11-12 10:52:51 +01:00
pr_info ( " Partition %d is %s offset %llx and length %llx \n " , i ,
parts [ i ] . name , parts [ i ] . offset , parts [ i ] . size ) ;
2011-12-05 16:08:08 +01:00
* pparts = parts ;
2015-12-13 22:49:26 +00:00
2011-12-05 16:08:08 +01:00
return nrparts ;
2015-12-13 22:52:15 +00:00
}
static int bcm63xx_parse_cfe_partitions ( struct mtd_info * master ,
const struct mtd_partition * * pparts ,
struct mtd_part_parser_data * data )
{
struct bcm963xx_nvram * nvram = NULL ;
int ret ;
if ( bcm63xx_detect_cfe ( master ) )
return - EINVAL ;
nvram = vzalloc ( sizeof ( * nvram ) ) ;
if ( ! nvram )
return - ENOMEM ;
ret = bcm63xx_read_nvram ( master , nvram ) ;
if ( ret )
goto out ;
if ( ! mtd_type_is_nand ( master ) )
ret = bcm63xx_parse_cfe_nor_partitions ( master , pparts , nvram ) ;
else
ret = - EINVAL ;
out :
vfree ( nvram ) ;
return ret ;
2011-12-05 16:08:08 +01:00
} ;
2019-03-28 15:19:07 +01:00
static const struct of_device_id parse_bcm63xx_cfe_match_table [ ] = {
{ . compatible = " brcm,bcm963xx-cfe-nor-partitions " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , parse_bcm63xx_cfe_match_table ) ;
2011-12-05 16:08:08 +01:00
static struct mtd_part_parser bcm63xx_cfe_parser = {
. parse_fn = bcm63xx_parse_cfe_partitions ,
. name = " bcm63xxpart " ,
2019-03-28 15:19:07 +01:00
. of_match_table = parse_bcm63xx_cfe_match_table ,
2011-12-05 16:08:08 +01:00
} ;
2015-11-11 19:13:30 -08:00
module_mtd_part_parser ( bcm63xx_cfe_parser ) ;
2011-12-05 16:08:08 +01:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Daniel Dickinson <openwrt@cshore.neomailbox.net> " ) ;
MODULE_AUTHOR ( " Florian Fainelli <florian@openwrt.org> " ) ;
MODULE_AUTHOR ( " Mike Albon <malbon@openwrt.org> " ) ;
MODULE_AUTHOR ( " Jonas Gorski <jonas.gorski@gmail.com " ) ;
MODULE_DESCRIPTION ( " MTD partitioning for BCM63XX CFE bootloaders " ) ;