| /* |
| * 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"); |