The CFE boot loader on BCM63XX platforms assumes itself and the NVRAM partition to be 64 KiB (or erase block sized, if larger). Ensure this assumption is also met when creating the partitions to prevent accidential erasure of CFE or NVRAM. Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com> Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@linux.intel.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
210 lines
5.6 KiB
C
210 lines
5.6 KiB
C
/*
|
|
* 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>
|
|
* Copyright © 2011 Jonas Gorski <jonas.gorski@gmail.com>
|
|
*
|
|
* 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
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/mtd/mtd.h>
|
|
#include <linux/mtd/partitions.h>
|
|
|
|
#include <asm/mach-bcm63xx/bcm963xx_tag.h>
|
|
#include <asm/mach-bcm63xx/board_bcm963xx.h>
|
|
|
|
#define BCM63XX_EXTENDED_SIZE 0xBFC00000 /* Extended flash address */
|
|
|
|
#define BCM63XX_MIN_CFE_SIZE 0x10000 /* always at least 64KiB */
|
|
#define BCM63XX_MIN_NVRAM_SIZE 0x10000 /* always at least 64KiB */
|
|
|
|
#define BCM63XX_CFE_MAGIC_OFFSET 0x4e0
|
|
|
|
static int bcm63xx_detect_cfe(struct mtd_info *master)
|
|
{
|
|
char buf[9];
|
|
int ret;
|
|
size_t retlen;
|
|
|
|
ret = master->read(master, BCM963XX_CFE_VERSION_OFFSET, 5, &retlen,
|
|
(void *)buf);
|
|
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 */
|
|
ret = master->read(master, BCM63XX_CFE_MAGIC_OFFSET, 8, &retlen,
|
|
(void *)buf);
|
|
buf[retlen] = 0;
|
|
|
|
return strncmp("CFE1CFE1", buf, 8);
|
|
}
|
|
|
|
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;
|
|
unsigned int cfelen, nvramlen;
|
|
int namelen = 0;
|
|
int i;
|
|
char *boardid;
|
|
char *tagversion;
|
|
|
|
if (bcm63xx_detect_cfe(master))
|
|
return -EINVAL;
|
|
|
|
cfelen = max_t(uint32_t, master->erasesize, BCM63XX_MIN_CFE_SIZE);
|
|
nvramlen = max_t(uint32_t, master->erasesize, BCM63XX_MIN_NVRAM_SIZE);
|
|
|
|
/* Allocate memory for buffer */
|
|
buf = vmalloc(sizeof(struct bcm_tag));
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
/* Get the tag */
|
|
ret = master->read(master, cfelen, sizeof(struct bcm_tag), &retlen,
|
|
(void *)buf);
|
|
|
|
if (retlen != sizeof(struct bcm_tag)) {
|
|
vfree(buf);
|
|
return -EIO;
|
|
}
|
|
|
|
sscanf(buf->kernel_address, "%u", &kerneladdr);
|
|
sscanf(buf->kernel_length, "%u", &kernellen);
|
|
sscanf(buf->total_length, "%u", &totallen);
|
|
tagversion = &(buf->tag_version[0]);
|
|
boardid = &(buf->board_id[0]);
|
|
|
|
pr_info("CFE boot tag found with version %s and board type %s\n",
|
|
tagversion, boardid);
|
|
|
|
kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE;
|
|
rootfsaddr = kerneladdr + kernellen;
|
|
spareaddr = roundup(totallen, master->erasesize) + cfelen;
|
|
sparelen = master->size - spareaddr - nvramlen;
|
|
rootfslen = spareaddr - rootfsaddr;
|
|
|
|
/* Determine number of partitions */
|
|
namelen = 8;
|
|
if (rootfslen > 0) {
|
|
nrparts++;
|
|
namelen += 6;
|
|
}
|
|
if (kernellen > 0) {
|
|
nrparts++;
|
|
namelen += 6;
|
|
}
|
|
|
|
/* 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;
|
|
parts[curpart].size = cfelen;
|
|
curpart++;
|
|
|
|
if (kernellen > 0) {
|
|
parts[curpart].name = "kernel";
|
|
parts[curpart].offset = kerneladdr;
|
|
parts[curpart].size = kernellen;
|
|
curpart++;
|
|
}
|
|
|
|
if (rootfslen > 0) {
|
|
parts[curpart].name = "rootfs";
|
|
parts[curpart].offset = rootfsaddr;
|
|
parts[curpart].size = rootfslen;
|
|
if (sparelen > 0)
|
|
parts[curpart].size += sparelen;
|
|
curpart++;
|
|
}
|
|
|
|
parts[curpart].name = "nvram";
|
|
parts[curpart].offset = master->size - nvramlen;
|
|
parts[curpart].size = nvramlen;
|
|
|
|
/* Global partition "linux" to make easy firmware upgrade */
|
|
curpart++;
|
|
parts[curpart].name = "linux";
|
|
parts[curpart].offset = parts[0].size;
|
|
parts[curpart].size = master->size - parts[0].size - parts[3].size;
|
|
|
|
for (i = 0; i < nrparts; i++)
|
|
pr_info("Partition %d is %s offset %lx and length %lx\n", i,
|
|
parts[i].name, (long unsigned int)(parts[i].offset),
|
|
(long unsigned int)(parts[i].size));
|
|
|
|
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)
|
|
{
|
|
return register_mtd_parser(&bcm63xx_cfe_parser);
|
|
}
|
|
|
|
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");
|