blob: d9e19570e9922f28bf1bf915894d7d5e7d33f80e [file] [log] [blame]
/*
* Copyright © 2013 Shawn Fisher <sfisher@cradlepoint.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
*
* CradlePoint flash partition table.
* Based on ar7part by Eugene Konev <ejka@openwrt.org>
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/bootmem.h>
#include <linux/module.h>
#include <uapi/linux/magic.h>
#define MTD_PARTS 8
#define NOR_KERN_OFFSET 0x80000
#define SPI_KERN_OFFSET 0x50000
#define KERN_MAGIC 0x27051956
#define ROOT_MAGIC 0x27051958
#define ALIGNMENT_PAD 16
struct cp_root_rec {
unsigned int magic; /* Image Header Magic Number */
unsigned int hcrc; /* Image Header CRC Checksum */
unsigned int size; /* Image Data Size */
};
struct cp_kern_rec {
unsigned int magic; /* Image Header Magic Number */
unsigned int hcrc; /* Image Header CRC Checksum */
unsigned int time; /* Image Creation Timestamp */
unsigned int size; /* Image Data Size */
unsigned int load; /* Data Load Address */
unsigned int ep; /* Entry Point Address */
unsigned int dcrc; /* Image Data CRC Checksum */
unsigned char os; /* Operating System */
unsigned char arch; /* CPU architecture */
unsigned char type; /* Image Type */
unsigned char comp; /* Compression Type */
unsigned char name[32]; /* Image Name */
};
static int create_mtd_partitions(struct mtd_info *master,
struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
/* struct cp_root_rec sqfs_header; */
struct cp_root_rec root_header;
struct cp_kern_rec ibr_kern_header, mbr_kern_header;
unsigned int root_offset = 0, root_size = 0;
unsigned int kern_offset = 0, kern_size = 0;
unsigned int upgrade_size = 0;
size_t len;
int rt3883 = 1;
struct mtd_partition *cp_parts;
pr_debug("%s() master->size : 0x%llx\n", __func__, master->size);
pr_debug("%s() master->erasesize 0x%x\n", __func__, master->erasesize);
mtd_read(master, NOR_KERN_OFFSET, sizeof(mbr_kern_header), &len, (u8 *)&mbr_kern_header);
if (ntohl(mbr_kern_header.magic) == KERN_MAGIC) {
/* kern_size = (ntohl(mbr_kern_header.size) + */
/* master->erasesize) & ~(master->erasesize - 1); */
kern_size = (ntohl(mbr_kern_header.size) + 0x20000) & ~(0x20000 - 1);
kern_offset = NOR_KERN_OFFSET;
rt3883 = 1;
}
mtd_read(master, SPI_KERN_OFFSET, sizeof(ibr_kern_header), &len, (u8 *)&ibr_kern_header);
if (ntohl(ibr_kern_header.magic) == KERN_MAGIC) {
/* kern_size = (ntohl(ibr_kern_header.size) + */
/* master->erasesize) & ~(master->erasesize - 1); */
kern_size = (ntohl(ibr_kern_header.size) + 0x20000) & ~(0x20000 - 1);
kern_offset = SPI_KERN_OFFSET;
rt3883 = 0;
}
pr_debug("Kernel Offset: 0x%x\n", kern_offset);
pr_debug("Kernel Header Size: 0x%x\n", kern_size);
if (!kern_size || !kern_offset) {
/* likely a memory build so just assume something safe */
kern_offset = NOR_KERN_OFFSET;
kern_size = master->erasesize;
}
root_offset = kern_offset + kern_size;
mtd_read(master, root_offset - ALIGNMENT_PAD, sizeof(root_header),
&len, (u8 *)&root_header);
if (ntohl(root_header.magic) == ROOT_MAGIC) {
/* root_size = (root_header.size + */
/* master->erasesize) & ~(master->erasesize - 1); */
root_size = (root_header.size + 0x20000) & ~(0x20000 - 1);
} else {
root_size = master->erasesize;
}
#if 0
do {
mtd_read(master, root_offset, sizeof(root_header),
&len, (u8 *)&root_header);
if (ntohl(root_header.magic) == ROOT_MAGIC) {
root_size = (root_header.size +
master->erasesize) & ~(master->erasesize - 1);
break;
}
printk("Root Magic: %x\n", ntohl(root_header.magic));
printk("Root Header Size: %x\n", root_size);
printk("Root Offset: %x\n", root_offset);
root_offset += ALIGNMENT_PAD;
}
while (root_header.magic == 0xffffffff);
#endif
pr_debug("Root Offset: 0x%x\n", root_offset);
pr_debug("Root Header Size: 0x%x\n", root_size);
upgrade_size = master->size - (kern_size + root_size +
(rt3883 ? NOR_KERN_OFFSET : SPI_KERN_OFFSET) +
(rt3883 ? master->erasesize * 4 : master->erasesize * 8));
pr_debug("Upgrade Offset: 0x%x\n", MTDPART_OFS_APPEND);
pr_debug("Upgrade Size: 0x%x\n", upgrade_size);
/* mtd_read(master, root_offset, sizeof(sqfs_header), &len, (u8 *)&sqfs_header); */
/* printk("SquashFS Magic: %x\n", sqfs_header.magic); */
cp_parts = kzalloc(sizeof(*cp_parts) * MTD_PARTS, GFP_KERNEL);
if (!cp_parts)
return -ENOMEM;
cp_parts[0].name = "Bootloader";
cp_parts[0].offset = 0;
cp_parts[0].size = master->erasesize * (rt3883 ? 2 : 3);
cp_parts[0].mask_flags = 0;
cp_parts[1].name = "Config";
cp_parts[1].offset = MTDPART_OFS_APPEND;
cp_parts[1].size = master->erasesize;
cp_parts[1].mask_flags = 0;
cp_parts[2].name = "Factory";
cp_parts[2].offset = MTDPART_OFS_APPEND;
cp_parts[2].size = master->erasesize;
cp_parts[2].mask_flags = 0;
cp_parts[3].name = "Kernel";
cp_parts[3].offset = kern_offset;
cp_parts[3].size = kern_size;
cp_parts[3].mask_flags = 0;
cp_parts[4].name = "RootFS";
cp_parts[4].offset = root_offset;
cp_parts[4].size = root_size;
cp_parts[4].mask_flags = 0;
cp_parts[5].name = "Upgrade";
cp_parts[5].offset = root_offset + root_size;
cp_parts[5].size = upgrade_size;
cp_parts[5].mask_flags = 0;
cp_parts[6].name = "2nd Filemanager";
cp_parts[6].offset = master->size - (master->erasesize * (rt3883 ? 4 : 8));
cp_parts[6].size = master->erasesize * (rt3883 ? 2 : 4);
cp_parts[6].mask_flags = 0;
cp_parts[7].name = "Filemanager";
cp_parts[7].offset = master->size - (master->erasesize * (rt3883 ? 2 : 4));
cp_parts[7].size = master->erasesize * (rt3883 ? 2: 4);
cp_parts[7].mask_flags = 0;
*pparts = cp_parts;
return MTD_PARTS;
}
static struct mtd_part_parser cp_parser = {
.owner = THIS_MODULE,
.parse_fn = create_mtd_partitions,
.name = "cppart",
};
static int __init cp_parser_init(void)
{
register_mtd_parser(&cp_parser);
return 0;
}
static void __exit cp_parser_exit(void)
{
deregister_mtd_parser(&cp_parser);
}
module_init(cp_parser_init);
module_exit(cp_parser_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Shawn Fisher <sfisher@cradlepoint.com>");
MODULE_DESCRIPTION("MTD partitioning for Cradlepoint Routers");