2011-12-05 19:08:08 +04: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 17:07:49 +04:00
* Copyright © 2011 - 2013 Jonas Gorski < jonas . gorski @ gmail . com >
2011-12-05 19:08:08 +04:00
*
* 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
*
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2011-12-17 16:58:18 +04:00
# include <linux/crc32.h>
2011-12-05 19:08:08 +04:00
# include <linux/module.h>
# include <linux/kernel.h>
2013-03-23 17:07:47 +04:00
# include <linux/sizes.h>
2011-12-05 19:08:08 +04:00
# include <linux/slab.h>
# include <linux/vmalloc.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/partitions.h>
2013-03-23 17:07:49 +04:00
# include <asm/mach-bcm63xx/bcm63xx_nvram.h>
2011-12-05 19:08:08 +04:00
# include <asm/mach-bcm63xx/bcm963xx_tag.h>
2011-12-17 16:58:14 +04:00
# include <asm/mach-bcm63xx/board_bcm963xx.h>
2011-12-05 19:08:08 +04:00
# define BCM63XX_EXTENDED_SIZE 0xBFC00000 /* Extended flash address */
2013-03-23 17:07:47 +04:00
# define BCM63XX_CFE_BLOCK_SIZE SZ_64K /* always at least 64KiB */
2011-12-19 14:36:04 +04:00
2011-12-17 16:58:14 +04:00
# define BCM63XX_CFE_MAGIC_OFFSET 0x4e0
2011-12-05 19:08:08 +04:00
static int bcm63xx_detect_cfe ( struct mtd_info * master )
{
char buf [ 9 ] ;
int ret ;
size_t retlen ;
2011-12-23 19:30:16 +04:00
ret = mtd_read ( master , BCM963XX_CFE_VERSION_OFFSET , 5 , & retlen ,
( void * ) buf ) ;
2011-12-17 16:58:14 +04: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 */
2011-12-23 19:30:16 +04:00
ret = mtd_read ( master , BCM63XX_CFE_MAGIC_OFFSET , 8 , & retlen ,
( void * ) buf ) ;
2011-12-05 19:08:08 +04:00
buf [ retlen ] = 0 ;
2011-12-17 16:58:14 +04:00
return strncmp ( " CFE1CFE1 " , buf , 8 ) ;
2011-12-05 19:08:08 +04:00
}
static int bcm63xx_parse_cfe_partitions ( struct mtd_info * master ,
struct mtd_partition * * pparts ,
struct mtd_part_parser_data * data )
{
/* CFE, NVRAM and global Linux are always present */
int nrparts = 3 , curpart = 0 ;
struct bcm_tag * buf ;
struct mtd_partition * parts ;
int ret ;
size_t retlen ;
unsigned int rootfsaddr , kerneladdr , spareaddr ;
unsigned int rootfslen , kernellen , sparelen , totallen ;
2011-12-19 14:36:04 +04:00
unsigned int cfelen , nvramlen ;
2012-11-12 13:52:49 +04:00
unsigned int cfe_erasesize ;
2011-12-05 19:08:08 +04:00
int i ;
2011-12-17 16:58:18 +04:00
u32 computed_crc ;
2012-04-19 15:15:57 +04:00
bool rootfs_first = false ;
2011-12-05 19:08:08 +04:00
if ( bcm63xx_detect_cfe ( master ) )
return - EINVAL ;
2012-11-12 13:52:49 +04:00
cfe_erasesize = max_t ( uint32_t , master - > erasesize ,
BCM63XX_CFE_BLOCK_SIZE ) ;
cfelen = cfe_erasesize ;
2013-03-23 17:07:49 +04:00
nvramlen = bcm63xx_nvram_get_psi_size ( ) * SZ_1K ;
nvramlen = roundup ( nvramlen , cfe_erasesize ) ;
2011-12-19 14:36:04 +04:00
2011-12-05 19:08:08 +04:00
/* Allocate memory for buffer */
buf = vmalloc ( sizeof ( struct bcm_tag ) ) ;
if ( ! buf )
return - ENOMEM ;
/* Get the tag */
2011-12-23 19:30:16 +04:00
ret = mtd_read ( master , cfelen , sizeof ( struct bcm_tag ) , & retlen ,
( void * ) buf ) ;
2011-12-19 14:36:04 +04:00
2011-12-05 19:08:08 +04:00
if ( retlen ! = sizeof ( struct bcm_tag ) ) {
vfree ( buf ) ;
return - EIO ;
}
2011-12-17 16:58:18 +04:00
computed_crc = crc32_le ( IMAGETAG_CRC_START , ( u8 * ) buf ,
offsetof ( struct bcm_tag , header_crc ) ) ;
if ( computed_crc = = buf - > header_crc ) {
char * boardid = & ( buf - > board_id [ 0 ] ) ;
char * tagversion = & ( buf - > tag_version [ 0 ] ) ;
2012-04-19 15:15:57 +04:00
sscanf ( buf - > flash_image_start , " %u " , & rootfsaddr ) ;
2011-12-17 16:58:18 +04:00
sscanf ( buf - > kernel_address , " %u " , & kerneladdr ) ;
sscanf ( buf - > kernel_length , " %u " , & kernellen ) ;
sscanf ( buf - > total_length , " %u " , & totallen ) ;
pr_info ( " CFE boot tag found with version %s and board type %s \n " ,
tagversion , boardid ) ;
kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE ;
2012-04-19 15:15:57 +04:00
rootfsaddr = rootfsaddr - BCM63XX_EXTENDED_SIZE ;
2011-12-17 16:58:18 +04:00
spareaddr = roundup ( totallen , master - > erasesize ) + cfelen ;
2012-04-19 15:15:57 +04:00
if ( rootfsaddr < kerneladdr ) {
/* default Broadcom layout */
rootfslen = kerneladdr - rootfsaddr ;
rootfs_first = true ;
} else {
/* OpenWrt layout */
rootfsaddr = kerneladdr + kernellen ;
rootfslen = spareaddr - rootfsaddr ;
}
2011-12-17 16:58:18 +04:00
} else {
pr_warn ( " CFE boot tag CRC invalid (expected %08x, actual %08x) \n " ,
buf - > header_crc , computed_crc ) ;
kernellen = 0 ;
rootfslen = 0 ;
rootfsaddr = 0 ;
spareaddr = cfelen ;
}
2012-11-12 13:52:48 +04:00
sparelen = master - > size - spareaddr - nvramlen ;
2011-12-05 19:08:08 +04:00
/* Determine number of partitions */
2012-11-12 13:52:47 +04:00
if ( rootfslen > 0 )
2011-12-05 19:08:08 +04:00
nrparts + + ;
2012-11-12 13:52:47 +04:00
if ( kernellen > 0 )
2011-12-05 19:08:08 +04:00
nrparts + + ;
/* Ask kernel for more memory */
parts = kzalloc ( sizeof ( * parts ) * nrparts + 10 * nrparts , GFP_KERNEL ) ;
if ( ! parts ) {
vfree ( buf ) ;
return - ENOMEM ;
}
/* Start building partition list */
parts [ curpart ] . name = " CFE " ;
parts [ curpart ] . offset = 0 ;
2011-12-19 14:36:04 +04:00
parts [ curpart ] . size = cfelen ;
2011-12-05 19:08:08 +04:00
curpart + + ;
if ( kernellen > 0 ) {
2012-04-19 15:15:57 +04:00
int kernelpart = curpart ;
if ( rootfslen > 0 & & rootfs_first )
kernelpart + + ;
parts [ kernelpart ] . name = " kernel " ;
parts [ kernelpart ] . offset = kerneladdr ;
parts [ kernelpart ] . size = kernellen ;
2011-12-05 19:08:08 +04:00
curpart + + ;
}
if ( rootfslen > 0 ) {
2012-04-19 15:15:57 +04:00
int rootfspart = curpart ;
if ( kernellen > 0 & & rootfs_first )
rootfspart - - ;
parts [ rootfspart ] . name = " rootfs " ;
parts [ rootfspart ] . offset = rootfsaddr ;
parts [ rootfspart ] . size = rootfslen ;
if ( sparelen > 0 & & ! rootfs_first )
parts [ rootfspart ] . size + = sparelen ;
2011-12-05 19:08:08 +04:00
curpart + + ;
}
parts [ curpart ] . name = " nvram " ;
2011-12-19 14:36:04 +04:00
parts [ curpart ] . offset = master - > size - nvramlen ;
parts [ curpart ] . size = nvramlen ;
2012-11-12 13:52:50 +04:00
curpart + + ;
2011-12-05 19:08:08 +04:00
/* Global partition "linux" to make easy firmware upgrade */
parts [ curpart ] . name = " linux " ;
2011-12-17 16:58:16 +04:00
parts [ curpart ] . offset = cfelen ;
parts [ curpart ] . size = master - > size - cfelen - nvramlen ;
2011-12-05 19:08:08 +04:00
for ( i = 0 ; i < nrparts ; i + + )
2012-11-12 13:52:51 +04: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 19:08:08 +04:00
pr_info ( " Spare partition is offset %x and length %x \n " , spareaddr ,
sparelen ) ;
* pparts = parts ;
vfree ( buf ) ;
return nrparts ;
} ;
static struct mtd_part_parser bcm63xx_cfe_parser = {
. owner = THIS_MODULE ,
. parse_fn = bcm63xx_parse_cfe_partitions ,
. name = " bcm63xxpart " ,
} ;
static int __init bcm63xx_cfe_parser_init ( void )
{
2013-12-01 15:01:06 +04:00
register_mtd_parser ( & bcm63xx_cfe_parser ) ;
return 0 ;
2011-12-05 19:08:08 +04:00
}
static void __exit bcm63xx_cfe_parser_exit ( void )
{
deregister_mtd_parser ( & bcm63xx_cfe_parser ) ;
}
module_init ( bcm63xx_cfe_parser_init ) ;
module_exit ( bcm63xx_cfe_parser_exit ) ;
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 " ) ;