diff options
Diffstat (limited to 'drivers/mtd/bcm63xxpart.c')
-rw-r--r-- | drivers/mtd/bcm63xxpart.c | 182 |
1 files changed, 141 insertions, 41 deletions
diff --git a/drivers/mtd/bcm63xxpart.c b/drivers/mtd/bcm63xxpart.c index cec3188a170d..41d1d3149c61 100644 --- a/drivers/mtd/bcm63xxpart.c +++ b/drivers/mtd/bcm63xxpart.c @@ -24,6 +24,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/bcm963xx_nvram.h> #include <linux/bcm963xx_tag.h> #include <linux/crc32.h> #include <linux/module.h> @@ -34,12 +35,15 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> -#include <asm/mach-bcm63xx/bcm63xx_nvram.h> -#include <asm/mach-bcm63xx/board_bcm963xx.h> +#define BCM963XX_CFE_BLOCK_SIZE SZ_64K /* always at least 64KiB */ -#define BCM63XX_CFE_BLOCK_SIZE SZ_64K /* always at least 64KiB */ +#define BCM963XX_CFE_MAGIC_OFFSET 0x4e0 +#define BCM963XX_CFE_VERSION_OFFSET 0x570 +#define BCM963XX_NVRAM_OFFSET 0x580 -#define BCM63XX_CFE_MAGIC_OFFSET 0x4e0 +/* 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) static int bcm63xx_detect_cfe(struct mtd_info *master) { @@ -58,68 +62,130 @@ static int bcm63xx_detect_cfe(struct mtd_info *master) return 0; /* very old CFE's do not have the cfe-v string, so check for magic */ - ret = mtd_read(master, BCM63XX_CFE_MAGIC_OFFSET, 8, &retlen, + ret = mtd_read(master, BCM963XX_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, - const struct mtd_partition **pparts, - struct mtd_part_parser_data *data) +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; +} + +static int bcm63xx_read_image_tag(struct mtd_info *master, const char *name, + loff_t tag_offset, struct bcm_tag *buf) +{ + int ret; + size_t retlen; + u32 computed_crc; + + ret = mtd_read(master, tag_offset, sizeof(*buf), &retlen, (void *)buf); + if (ret) + return ret; + + if (retlen != sizeof(*buf)) + return -EIO; + + computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)buf, + offsetof(struct bcm_tag, header_crc)); + if (computed_crc == buf->header_crc) { + STR_NULL_TERMINATE(buf->board_id); + STR_NULL_TERMINATE(buf->tag_version); + + pr_info("%s: CFE image tag found at 0x%llx with version %s, board type %s\n", + name, tag_offset, buf->tag_version, buf->board_id); + + return 0; + } + + pr_warn("%s: CFE image tag at 0x%llx CRC invalid (expected %08x, actual %08x)\n", + name, tag_offset, buf->header_crc, computed_crc); + return 1; +} + +static int bcm63xx_parse_cfe_nor_partitions(struct mtd_info *master, + const struct mtd_partition **pparts, struct bcm963xx_nvram *nvram) { /* CFE, NVRAM and global Linux are always present */ int nrparts = 3, curpart = 0; - struct bcm_tag *buf; + struct bcm_tag *buf = NULL; struct mtd_partition *parts; int ret; - size_t retlen; unsigned int rootfsaddr, kerneladdr, spareaddr; unsigned int rootfslen, kernellen, sparelen, totallen; unsigned int cfelen, nvramlen; unsigned int cfe_erasesize; int i; - u32 computed_crc; bool rootfs_first = false; - if (bcm63xx_detect_cfe(master)) - return -EINVAL; - cfe_erasesize = max_t(uint32_t, master->erasesize, - BCM63XX_CFE_BLOCK_SIZE); + BCM963XX_CFE_BLOCK_SIZE); cfelen = cfe_erasesize; - nvramlen = bcm63xx_nvram_get_psi_size() * SZ_1K; + nvramlen = nvram->psi_size * SZ_1K; nvramlen = roundup(nvramlen, cfe_erasesize); - /* Allocate memory for buffer */ buf = vmalloc(sizeof(struct bcm_tag)); if (!buf) return -ENOMEM; /* Get the tag */ - ret = mtd_read(master, cfelen, sizeof(struct bcm_tag), &retlen, - (void *)buf); - - if (retlen != sizeof(struct bcm_tag)) { - vfree(buf); - return -EIO; - } + ret = bcm63xx_read_image_tag(master, "rootfs", cfelen, buf); + if (!ret) { + STR_NULL_TERMINATE(buf->flash_image_start); + if (kstrtouint(buf->flash_image_start, 10, &rootfsaddr) || + rootfsaddr < BCM963XX_EXTENDED_SIZE) { + pr_err("invalid rootfs address: %*ph\n", + (int)sizeof(buf->flash_image_start), + buf->flash_image_start); + goto invalid_tag; + } - 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]); + STR_NULL_TERMINATE(buf->kernel_address); + if (kstrtouint(buf->kernel_address, 10, &kerneladdr) || + kerneladdr < BCM963XX_EXTENDED_SIZE) { + pr_err("invalid kernel address: %*ph\n", + (int)sizeof(buf->kernel_address), + buf->kernel_address); + goto invalid_tag; + } - sscanf(buf->flash_image_start, "%u", &rootfsaddr); - sscanf(buf->kernel_address, "%u", &kerneladdr); - sscanf(buf->kernel_length, "%u", &kernellen); - sscanf(buf->total_length, "%u", &totallen); + STR_NULL_TERMINATE(buf->kernel_length); + if (kstrtouint(buf->kernel_length, 10, &kernellen)) { + pr_err("invalid kernel length: %*ph\n", + (int)sizeof(buf->kernel_length), + buf->kernel_length); + goto invalid_tag; + } - pr_info("CFE boot tag found with version %s and board type %s\n", - tagversion, boardid); + STR_NULL_TERMINATE(buf->total_length); + if (kstrtouint(buf->total_length, 10, &totallen)) { + pr_err("invalid total length: %*ph\n", + (int)sizeof(buf->total_length), + buf->total_length); + goto invalid_tag; + } kerneladdr = kerneladdr - BCM963XX_EXTENDED_SIZE; rootfsaddr = rootfsaddr - BCM963XX_EXTENDED_SIZE; @@ -134,13 +200,14 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, rootfsaddr = kerneladdr + kernellen; rootfslen = spareaddr - rootfsaddr; } - } else { - pr_warn("CFE boot tag CRC invalid (expected %08x, actual %08x)\n", - buf->header_crc, computed_crc); + } else if (ret > 0) { +invalid_tag: kernellen = 0; rootfslen = 0; rootfsaddr = 0; spareaddr = cfelen; + } else { + goto out; } sparelen = master->size - spareaddr - nvramlen; @@ -151,11 +218,10 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, if (kernellen > 0) nrparts++; - /* Ask kernel for more memory */ parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL); if (!parts) { - vfree(buf); - return -ENOMEM; + ret = -ENOMEM; + goto out; } /* Start building partition list */ @@ -206,9 +272,43 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, sparelen); *pparts = parts; + ret = 0; + +out: vfree(buf); + if (ret) + return ret; + return nrparts; +} + +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; }; static struct mtd_part_parser bcm63xx_cfe_parser = { |