0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0012
0013 #include <linux/bcm963xx_tag.h>
0014 #include <linux/crc32.h>
0015 #include <linux/module.h>
0016 #include <linux/kernel.h>
0017 #include <linux/sizes.h>
0018 #include <linux/slab.h>
0019 #include <linux/vmalloc.h>
0020 #include <linux/mtd/mtd.h>
0021 #include <linux/mtd/partitions.h>
0022 #include <linux/of.h>
0023
0024
0025 #define STR_NULL_TERMINATE(x) \
0026 do { char *_str = (x); _str[sizeof(x) - 1] = 0; } while (0)
0027
0028 static int bcm963xx_read_imagetag(struct mtd_info *master, const char *name,
0029 loff_t tag_offset, struct bcm_tag *buf)
0030 {
0031 int ret;
0032 size_t retlen;
0033 u32 computed_crc;
0034
0035 ret = mtd_read(master, tag_offset, sizeof(*buf), &retlen, (void *)buf);
0036 if (ret)
0037 return ret;
0038
0039 if (retlen != sizeof(*buf))
0040 return -EIO;
0041
0042 computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)buf,
0043 offsetof(struct bcm_tag, header_crc));
0044 if (computed_crc == buf->header_crc) {
0045 STR_NULL_TERMINATE(buf->board_id);
0046 STR_NULL_TERMINATE(buf->tag_version);
0047
0048 pr_info("%s: CFE image tag found at 0x%llx with version %s, board type %s\n",
0049 name, tag_offset, buf->tag_version, buf->board_id);
0050
0051 return 0;
0052 }
0053
0054 pr_warn("%s: CFE image tag at 0x%llx CRC invalid (expected %08x, actual %08x)\n",
0055 name, tag_offset, buf->header_crc, computed_crc);
0056 return -EINVAL;
0057 }
0058
0059 static int bcm963xx_parse_imagetag_partitions(struct mtd_info *master,
0060 const struct mtd_partition **pparts,
0061 struct mtd_part_parser_data *data)
0062 {
0063
0064 int nrparts = 0, curpart = 0;
0065 struct bcm_tag *buf = NULL;
0066 struct mtd_partition *parts;
0067 int ret;
0068 unsigned int rootfsaddr, kerneladdr, spareaddr, offset;
0069 unsigned int rootfslen, kernellen, sparelen, totallen;
0070 int i;
0071 bool rootfs_first = false;
0072
0073 buf = vmalloc(sizeof(struct bcm_tag));
0074 if (!buf)
0075 return -ENOMEM;
0076
0077
0078 ret = bcm963xx_read_imagetag(master, "rootfs", 0, buf);
0079 if (!ret) {
0080 STR_NULL_TERMINATE(buf->flash_image_start);
0081 if (kstrtouint(buf->flash_image_start, 10, &rootfsaddr) ||
0082 rootfsaddr < BCM963XX_EXTENDED_SIZE) {
0083 pr_err("invalid rootfs address: %*ph\n",
0084 (int)sizeof(buf->flash_image_start),
0085 buf->flash_image_start);
0086 ret = -EINVAL;
0087 goto out;
0088 }
0089
0090 STR_NULL_TERMINATE(buf->kernel_address);
0091 if (kstrtouint(buf->kernel_address, 10, &kerneladdr) ||
0092 kerneladdr < BCM963XX_EXTENDED_SIZE) {
0093 pr_err("invalid kernel address: %*ph\n",
0094 (int)sizeof(buf->kernel_address),
0095 buf->kernel_address);
0096 ret = -EINVAL;
0097 goto out;
0098 }
0099
0100 STR_NULL_TERMINATE(buf->kernel_length);
0101 if (kstrtouint(buf->kernel_length, 10, &kernellen)) {
0102 pr_err("invalid kernel length: %*ph\n",
0103 (int)sizeof(buf->kernel_length),
0104 buf->kernel_length);
0105 ret = -EINVAL;
0106 goto out;
0107 }
0108
0109 STR_NULL_TERMINATE(buf->total_length);
0110 if (kstrtouint(buf->total_length, 10, &totallen)) {
0111 pr_err("invalid total length: %*ph\n",
0112 (int)sizeof(buf->total_length),
0113 buf->total_length);
0114 ret = -EINVAL;
0115 goto out;
0116 }
0117
0118
0119
0120
0121
0122
0123 if (rootfsaddr < kerneladdr)
0124 offset = rootfsaddr - sizeof(struct bcm_tag);
0125 else
0126 offset = kerneladdr - sizeof(struct bcm_tag);
0127
0128 kerneladdr = kerneladdr - offset;
0129 rootfsaddr = rootfsaddr - offset;
0130 spareaddr = roundup(totallen, master->erasesize);
0131
0132 if (rootfsaddr < kerneladdr) {
0133
0134 rootfslen = kerneladdr - rootfsaddr;
0135 rootfs_first = true;
0136 } else {
0137
0138 rootfsaddr = kerneladdr + kernellen;
0139 rootfslen = spareaddr - rootfsaddr;
0140 }
0141 } else {
0142 goto out;
0143 }
0144 sparelen = master->size - spareaddr;
0145
0146
0147 if (rootfslen > 0)
0148 nrparts++;
0149
0150 if (kernellen > 0)
0151 nrparts++;
0152
0153 parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL);
0154 if (!parts) {
0155 ret = -ENOMEM;
0156 goto out;
0157 }
0158
0159
0160 if (kernellen > 0) {
0161 int kernelpart = curpart;
0162
0163 if (rootfslen > 0 && rootfs_first)
0164 kernelpart++;
0165 parts[kernelpart].name = "kernel";
0166 parts[kernelpart].offset = kerneladdr;
0167 parts[kernelpart].size = kernellen;
0168 curpart++;
0169 }
0170
0171 if (rootfslen > 0) {
0172 int rootfspart = curpart;
0173
0174 if (kernellen > 0 && rootfs_first)
0175 rootfspart--;
0176 parts[rootfspart].name = "rootfs";
0177 parts[rootfspart].offset = rootfsaddr;
0178 parts[rootfspart].size = rootfslen;
0179 if (sparelen > 0 && !rootfs_first)
0180 parts[rootfspart].size += sparelen;
0181 curpart++;
0182 }
0183
0184 for (i = 0; i < nrparts; i++)
0185 pr_info("Partition %d is %s offset %llx and length %llx\n", i,
0186 parts[i].name, parts[i].offset, parts[i].size);
0187
0188 pr_info("Spare partition is offset %x and length %x\n", spareaddr,
0189 sparelen);
0190
0191 *pparts = parts;
0192 ret = 0;
0193
0194 out:
0195 vfree(buf);
0196
0197 if (ret)
0198 return ret;
0199
0200 return nrparts;
0201 }
0202
0203 static const struct of_device_id parse_bcm963xx_imagetag_match_table[] = {
0204 { .compatible = "brcm,bcm963xx-imagetag" },
0205 {},
0206 };
0207 MODULE_DEVICE_TABLE(of, parse_bcm963xx_imagetag_match_table);
0208
0209 static struct mtd_part_parser bcm963xx_imagetag_parser = {
0210 .parse_fn = bcm963xx_parse_imagetag_partitions,
0211 .name = "bcm963xx-imagetag",
0212 .of_match_table = parse_bcm963xx_imagetag_match_table,
0213 };
0214 module_mtd_part_parser(bcm963xx_imagetag_parser);
0215
0216 MODULE_LICENSE("GPL");
0217 MODULE_AUTHOR("Daniel Dickinson <openwrt@cshore.neomailbox.net>");
0218 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
0219 MODULE_AUTHOR("Mike Albon <malbon@openwrt.org>");
0220 MODULE_AUTHOR("Jonas Gorski <jonas.gorski@gmail.com>");
0221 MODULE_DESCRIPTION("MTD parser for BCM963XX CFE Image Tag partitions");