openwrtv4/target/linux/brcm63xx/patches-3.14/425-bcm63xxpart_parse_paritions_from_dt.patch
Jonas Gorski eed031d460 brcm63xx: brcm63xxpart: fix buffer overrun in of parser
We never account for the added partitions from the tag parser, so
allocated two partioness less than needed. This can cause issues when
the memory gets reused before the mtd code can actually add the
partitions.

Fixes oopses like:
[    0.920000] mtd: device 2 (rootfs) set to be root filesystem
[    0.924000] 1 squashfs-split partitions found on MTD device rootfs
[    0.932000] 0x0000002e0000-0x0000003f0000 : "rootfs_data"
[    0.940000] 0x000000010000-0x0000003f0000 : "linux"
[    0.948000] CPU 0 Unable to handle kernel paging request at virtual address 00010203, epc == 8028ddf8, ra == 801ad5d4
[    0.956000] Oops[#1]:
[    0.956000] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.18.8 #22
[    0.956000] task: 8181da78 ti: 8181e000 task.ti: 8181e000
[    0.956000] $ 0   : 00000000 10008f00 00010203 81049e40
[    0.956000] $ 4   : 00010203 000000d0 00000000 000000fd
[    0.956000] $ 8   : 00000000 80015490 00000000 80370000
[    0.956000] $12   : 00000000 00000001 00000000 2f736269
[    0.956000] $16   : 00010203 000000d0 8007bde0 81482000
[    0.956000] $20   : 00000000 00000004 81481f80 803c4b78
[    0.956000] $24   : 00000000 00000000
[    0.956000] $28   : 8181e000 8181fbe8 80b20000 801ad5d4
[    0.956000] Hi    : 00000000
[    0.956000] Lo    : 00000000
[    0.956000] epc   : 8028ddf8 strlen+0x4/0x20
[    0.956000]     Not tainted
[    0.956000] ra    : 801ad5d4 kstrdup+0x30/0x84
[    0.956000] Status: 10008f03 KERNEL EXL IE
[    0.956000] Cause : 00800008
[    0.956000] BadVA : 00010203
[    0.956000] PrId  : 0002a010 (Broadcom BMIPS4350)
[    0.956000] Modules linked in:
[    0.956000] Process swapper/0 (pid: 1, threadinfo=8181e000, task=8181da78, tls=00000000)
[    0.956000] Stack : 8148aa00 803c4bcc 00000000 801cd9d0 00000000 8148a400 818a1400 003f0000
          81482000 8007bde0 8148aa00 800ac78c 8148aa00 00000004 8037ae30 8181fc4c
          8148aa00 803bc074 803c0000 80074b84 00000000 00010000 00000004 ffffffff
          8037ae30 00000004 8148aa00 00000004 803c4b80 818a1400 803c0000 00000005
          81481f80 80074ce0 8181fc84 00000005 81879800 8008ece4 00000000 003f0000
          ...
[    0.956000] Call Trace:
[    0.956000] [<8028ddf8>] strlen+0x4/0x20
[    0.956000] [<801ad5d4>] kstrdup+0x30/0x84
[    0.956000] [<8007bde0>] allocate_partition+0x58/0x93c
[    0.956000] [<80074ce0>] add_mtd_partitions+0x90/0x144
[    0.956000] [<801cc228>] mtd_device_parse_register+0x78/0xcc
[    0.956000] [<801e7870>] of_flash_probe+0x658/0x728
[    0.956000] [<80216ee0>] platform_drv_probe+0x24/0x60
[    0.956000] [<801221f4>] driver_probe_device+0xcc/0x214
[    0.956000] [<800466f8>] __driver_attach+0x7c/0xb4
[    0.956000] [<800c38bc>] bus_for_each_dev+0x94/0xa4
[    0.956000] [<800c35e4>] bus_add_driver+0x104/0x1ec
[    0.956000] [<80122400>] driver_register+0xac/0x100
[    0.956000] [<80113a5c>] do_one_initcall+0x148/0x204
[    0.956000] [<803cbd1c>] kernel_init_freeable+0x168/0x220
[    0.956000] [<800157bc>] kernel_init+0x10/0x114
[    0.956000] [<800108a8>] ret_from_kernel_thread+0x14/0x1c
[    0.956000]
[    0.956000]
Code: 03e00008  27bd0028  00801021 <80430000> 10600003  00000000  080a377e  24420001  03e00008
[    1.192000] ---[ end trace 9d89ccbcae27fe40 ]---
[    1.200000] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
[    1.200000]
[    1.200000] ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b

Signed-off-by: Jonas Gorski <jogo@openwrt.org>

SVN-Revision: 44787
2015-03-15 14:19:22 +00:00

357 lines
9.2 KiB
Diff

--- a/drivers/mtd/bcm63xxpart.c
+++ b/drivers/mtd/bcm63xxpart.c
@@ -32,6 +32,7 @@
#include <linux/vmalloc.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
+#include <linux/of.h>
#include <asm/mach-bcm63xx/bcm63xx_nvram.h>
#include <asm/mach-bcm63xx/bcm963xx_tag.h>
@@ -43,66 +44,35 @@
#define BCM63XX_CFE_MAGIC_OFFSET 0x4e0
-static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
- struct mtd_partition **pparts,
- struct mtd_part_parser_data *data)
+static bool node_has_compatible(struct device_node *pp)
+{
+ return of_get_property(pp, "compatible", NULL);
+}
+
+static int parse_bcmtag(struct mtd_info *master, struct mtd_partition *pparts,
+ int next_part, size_t offset, size_t size)
{
- /* CFE, NVRAM and global Linux are always present */
- int nrparts = 3, curpart = 0;
struct bcm_tag *buf;
- struct mtd_partition *parts;
+ u32 computed_crc;
int ret;
size_t retlen;
- unsigned int rootfsaddr, kerneladdr, spareaddr, nvramaddr;
- unsigned int rootfslen, kernellen, sparelen, totallen;
- unsigned int cfelen, nvramlen;
- unsigned int cfe_erasesize;
- unsigned int caldatalen1 = 0, caldataaddr1 = 0;
- unsigned int caldatalen2 = 0, caldataaddr2 = 0;
- int i;
- u32 computed_crc;
+ unsigned int rootfsaddr, kerneladdr;
+ unsigned int rootfslen, kernellen, totallen;
bool rootfs_first = false;
-
- if (!bcm63xx_is_cfe_present())
- return -EINVAL;
-
- cfe_erasesize = max_t(uint32_t, master->erasesize,
- BCM63XX_CFE_BLOCK_SIZE);
-
- cfelen = cfe_erasesize;
- nvramlen = bcm63xx_nvram_get_psi_size() * SZ_1K;
- nvramlen = roundup(nvramlen, cfe_erasesize);
- nvramaddr = master->size - nvramlen;
-
- if (data) {
- if (data->caldata[0]) {
- caldatalen1 = cfe_erasesize;
- caldataaddr1 = rounddown(data->caldata[0],
- cfe_erasesize);
- }
- if (data->caldata[1]) {
- caldatalen2 = cfe_erasesize;
- caldataaddr2 = rounddown(data->caldata[1],
- cfe_erasesize);
- }
- if (caldataaddr1 == caldataaddr2) {
- caldataaddr2 = 0;
- caldatalen2 = 0;
- }
- }
+ int curr_part = next_part;
/* Allocate memory for buffer */
- buf = vmalloc(sizeof(struct bcm_tag));
+ buf = vmalloc(sizeof(*buf));
if (!buf)
return -ENOMEM;
/* Get the tag */
- ret = mtd_read(master, cfelen, sizeof(struct bcm_tag), &retlen,
+ ret = mtd_read(master, offset, sizeof(*buf), &retlen,
(void *)buf);
- if (retlen != sizeof(struct bcm_tag)) {
+ if (retlen != sizeof(*buf)) {
vfree(buf);
- return -EIO;
+ return 0;
}
computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)buf,
@@ -121,7 +91,6 @@ static int bcm63xx_parse_cfe_partitions(
kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE;
rootfsaddr = rootfsaddr - BCM63XX_EXTENDED_SIZE;
- spareaddr = roundup(totallen, master->erasesize) + cfelen;
if (rootfsaddr < kerneladdr) {
/* default Broadcom layout */
@@ -130,8 +99,8 @@ static int bcm63xx_parse_cfe_partitions(
} else {
/* OpenWrt layout */
rootfsaddr = kerneladdr + kernellen;
- rootfslen = buf->real_rootfs_length;
- spareaddr = rootfsaddr + rootfslen;
+ rootfslen = size - kernellen -
+ sizeof(*buf);
}
} else {
pr_warn("CFE boot tag CRC invalid (expected %08x, actual %08x)\n",
@@ -139,16 +108,153 @@ static int bcm63xx_parse_cfe_partitions(
kernellen = 0;
rootfslen = 0;
rootfsaddr = 0;
- spareaddr = cfelen;
}
- sparelen = min_not_zero(nvramaddr, caldataaddr1) - spareaddr;
- /* Determine number of partitions */
- if (rootfslen > 0)
- nrparts++;
+ if (kernellen > 0) {
+ int kernelpart = curr_part;
- if (kernellen > 0)
- nrparts++;
+ if (rootfslen > 0 && rootfs_first)
+ kernelpart++;
+ pparts[kernelpart].name = "kernel";
+ pparts[kernelpart].offset = kerneladdr;
+ pparts[kernelpart].size = kernellen;
+ curr_part++;
+ }
+
+ if (rootfslen > 0) {
+ int rootfspart = curr_part;
+
+ if (kernellen > 0 && rootfs_first)
+ rootfspart--;
+ pparts[rootfspart].name = "rootfs";
+ pparts[rootfspart].offset = rootfsaddr;
+ pparts[rootfspart].size = rootfslen;
+
+ curr_part++;
+ }
+
+ vfree(buf);
+
+ return curr_part - next_part;
+}
+
+
+static int bcm63xx_parse_cfe_partitions_of(struct mtd_info *master,
+ struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+{
+ struct device_node *dp = data->of_node;
+ struct device_node *pp;
+ int i, nr_parts = 0;
+ const char *partname;
+ int len;
+
+ for_each_child_of_node(dp, pp) {
+ if (node_has_compatible(pp))
+ continue;
+
+ if (!of_get_property(pp, "reg", &len))
+ continue;
+
+ partname = of_get_property(pp, "label", &len);
+ if (!partname)
+ partname = of_get_property(pp, "name", &len);
+
+ if (!strcmp(partname, "linux"))
+ nr_parts += 2;
+
+ nr_parts++;
+ }
+
+ *pparts = kzalloc(nr_parts * sizeof(**pparts), GFP_KERNEL);
+ if (!*pparts)
+ return -ENOMEM;
+
+ i = 0;
+ for_each_child_of_node(dp, pp) {
+ const __be32 *reg;
+ int a_cells, s_cells;
+ size_t size, offset;
+
+ if (node_has_compatible(pp))
+ continue;
+
+ reg = of_get_property(pp, "reg", &len);
+ if (!reg)
+ continue;
+
+ a_cells = of_n_addr_cells(pp);
+ s_cells = of_n_size_cells(pp);
+ offset = of_read_number(reg, a_cells);
+ size = of_read_number(reg + a_cells, s_cells);
+ partname = of_get_property(pp, "label", &len);
+ if (!partname)
+ partname = of_get_property(pp, "name", &len);
+
+ if (!strcmp(partname, "linux"))
+ i += parse_bcmtag(master, *pparts, i, offset, size);
+
+ if (of_get_property(pp, "read-only", &len))
+ (*pparts)[i].mask_flags |= MTD_WRITEABLE;
+
+ if (of_get_property(pp, "lock", &len))
+ (*pparts)[i].mask_flags |= MTD_POWERUP_LOCK;
+
+ (*pparts)[i].offset = offset;
+ (*pparts)[i].size = size;
+ (*pparts)[i].name = partname;
+
+ i++;
+ }
+
+ return i;
+}
+
+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 = 5, curpart = 0;
+ struct mtd_partition *parts;
+ unsigned int nvramaddr;
+ unsigned int cfelen, nvramlen;
+ unsigned int cfe_erasesize;
+ unsigned int caldatalen1 = 0, caldataaddr1 = 0;
+ unsigned int caldatalen2 = 0, caldataaddr2 = 0;
+ unsigned int imageaddr, imagelen;
+ int i;
+
+ if (!bcm63xx_is_cfe_present())
+ return -EINVAL;
+
+ cfe_erasesize = max_t(uint32_t, master->erasesize,
+ BCM63XX_CFE_BLOCK_SIZE);
+
+ cfelen = cfe_erasesize;
+ nvramlen = bcm63xx_nvram_get_psi_size() * SZ_1K;
+ nvramlen = roundup(nvramlen, cfe_erasesize);
+ nvramaddr = master->size - nvramlen;
+
+ if (data) {
+ if (data->caldata[0]) {
+ caldatalen1 = cfe_erasesize;
+ caldataaddr1 = rounddown(data->caldata[0],
+ cfe_erasesize);
+ }
+ if (data->caldata[1]) {
+ caldatalen2 = cfe_erasesize;
+ caldataaddr2 = rounddown(data->caldata[1],
+ cfe_erasesize);
+ }
+ if (caldataaddr1 == caldataaddr2) {
+ caldataaddr2 = 0;
+ caldatalen2 = 0;
+ }
+ }
+
+ imageaddr = cfelen;
+ imagelen = min_not_zero(nvramaddr, caldataaddr1) - imageaddr;
if (caldatalen1 > 0)
nrparts++;
@@ -158,10 +264,8 @@ static int bcm63xx_parse_cfe_partitions(
/* Ask kernel for more memory */
parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL);
- if (!parts) {
- vfree(buf);
+ if (!parts)
return -ENOMEM;
- }
/* Start building partition list */
parts[curpart].name = "CFE";
@@ -169,29 +273,7 @@ static int bcm63xx_parse_cfe_partitions(
parts[curpart].size = cfelen;
curpart++;
- if (kernellen > 0) {
- int kernelpart = curpart;
-
- if (rootfslen > 0 && rootfs_first)
- kernelpart++;
- parts[kernelpart].name = "kernel";
- parts[kernelpart].offset = kerneladdr;
- parts[kernelpart].size = kernellen;
- curpart++;
- }
-
- if (rootfslen > 0) {
- int rootfspart = curpart;
-
- if (kernellen > 0 && rootfs_first)
- rootfspart--;
- parts[rootfspart].name = "rootfs";
- parts[rootfspart].offset = rootfsaddr;
- parts[rootfspart].size = rootfslen;
- if (sparelen > 0 && !rootfs_first)
- parts[rootfspart].size += sparelen;
- curpart++;
- }
+ curpart += parse_bcmtag(master, parts, curpart, imageaddr, imagelen);
if (caldatalen1 > 0) {
if (caldatalen2 > 0)
@@ -217,25 +299,33 @@ static int bcm63xx_parse_cfe_partitions(
/* Global partition "linux" to make easy firmware upgrade */
parts[curpart].name = "linux";
- parts[curpart].offset = cfelen;
- parts[curpart].size = min_not_zero(nvramaddr, caldataaddr1) - cfelen;
+ parts[curpart].offset = imageaddr;
+ parts[curpart].size = imagelen;
+ curpart++;
- for (i = 0; i < nrparts; i++)
+ for (i = 0; i < curpart; i++)
pr_info("Partition %d is %s offset %llx and length %llx\n", i,
parts[i].name, parts[i].offset, parts[i].size);
- pr_info("Spare partition is offset %x and length %x\n", spareaddr,
- sparelen);
-
*pparts = parts;
- vfree(buf);
return nrparts;
};
+
+static int bcm63xx_parse_partitions(struct mtd_info *master,
+ struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+{
+ if (data && data->of_node)
+ return bcm63xx_parse_cfe_partitions_of(master, pparts, data);
+ else
+ return bcm63xx_parse_cfe_partitions(master, pparts, data);
+}
+
static struct mtd_part_parser bcm63xx_cfe_parser = {
.owner = THIS_MODULE,
- .parse_fn = bcm63xx_parse_cfe_partitions,
+ .parse_fn = bcm63xx_parse_partitions,
.name = "bcm63xxpart",
};