summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/mtd/Kconfig1
-rw-r--r--drivers/mtd/bcm63xxpart.c155
-rw-r--r--drivers/mtd/parsers/Kconfig11
-rw-r--r--drivers/mtd/parsers/Makefile1
-rw-r--r--drivers/mtd/parsers/parser_imagetag.c214
5 files changed, 235 insertions, 147 deletions
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index aa5a27fdfdd1..0c263df7ded7 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -94,6 +94,7 @@ config MTD_BCM63XX_PARTS
94 tristate "BCM63XX CFE partitioning support" 94 tristate "BCM63XX CFE partitioning support"
95 depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST 95 depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST
96 select CRC32 96 select CRC32
97 select MTD_PARSER_IMAGETAG
97 help 98 help
98 This provides partition parsing for BCM63xx devices with CFE 99 This provides partition parsing for BCM63xx devices with CFE
99 bootloaders. 100 bootloaders.
diff --git a/drivers/mtd/bcm63xxpart.c b/drivers/mtd/bcm63xxpart.c
index f639b4c960f0..b2bd04764e95 100644
--- a/drivers/mtd/bcm63xxpart.c
+++ b/drivers/mtd/bcm63xxpart.c
@@ -94,51 +94,19 @@ static int bcm63xx_read_nvram(struct mtd_info *master,
94 return 0; 94 return 0;
95} 95}
96 96
97static int bcm63xx_read_image_tag(struct mtd_info *master, const char *name, 97static const char * const bcm63xx_cfe_part_types[] = {
98 loff_t tag_offset, struct bcm_tag *buf) 98 "bcm963xx-imagetag",
99{ 99 NULL,
100 int ret; 100};
101 size_t retlen;
102 u32 computed_crc;
103
104 ret = mtd_read(master, tag_offset, sizeof(*buf), &retlen, (void *)buf);
105 if (ret)
106 return ret;
107
108 if (retlen != sizeof(*buf))
109 return -EIO;
110
111 computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)buf,
112 offsetof(struct bcm_tag, header_crc));
113 if (computed_crc == buf->header_crc) {
114 STR_NULL_TERMINATE(buf->board_id);
115 STR_NULL_TERMINATE(buf->tag_version);
116
117 pr_info("%s: CFE image tag found at 0x%llx with version %s, board type %s\n",
118 name, tag_offset, buf->tag_version, buf->board_id);
119
120 return 0;
121 }
122
123 pr_warn("%s: CFE image tag at 0x%llx CRC invalid (expected %08x, actual %08x)\n",
124 name, tag_offset, buf->header_crc, computed_crc);
125 return 1;
126}
127 101
128static int bcm63xx_parse_cfe_nor_partitions(struct mtd_info *master, 102static int bcm63xx_parse_cfe_nor_partitions(struct mtd_info *master,
129 const struct mtd_partition **pparts, struct bcm963xx_nvram *nvram) 103 const struct mtd_partition **pparts, struct bcm963xx_nvram *nvram)
130{ 104{
131 /* CFE, NVRAM and global Linux are always present */
132 int nrparts = 3, curpart = 0;
133 struct bcm_tag *buf = NULL;
134 struct mtd_partition *parts; 105 struct mtd_partition *parts;
135 int ret; 106 int nrparts = 3, curpart = 0;
136 unsigned int rootfsaddr, kerneladdr, spareaddr;
137 unsigned int rootfslen, kernellen, sparelen, totallen;
138 unsigned int cfelen, nvramlen; 107 unsigned int cfelen, nvramlen;
139 unsigned int cfe_erasesize; 108 unsigned int cfe_erasesize;
140 int i; 109 int i;
141 bool rootfs_first = false;
142 110
143 cfe_erasesize = max_t(uint32_t, master->erasesize, 111 cfe_erasesize = max_t(uint32_t, master->erasesize,
144 BCM963XX_CFE_BLOCK_SIZE); 112 BCM963XX_CFE_BLOCK_SIZE);
@@ -147,83 +115,9 @@ static int bcm63xx_parse_cfe_nor_partitions(struct mtd_info *master,
147 nvramlen = nvram->psi_size * SZ_1K; 115 nvramlen = nvram->psi_size * SZ_1K;
148 nvramlen = roundup(nvramlen, cfe_erasesize); 116 nvramlen = roundup(nvramlen, cfe_erasesize);
149 117
150 buf = vmalloc(sizeof(struct bcm_tag));
151 if (!buf)
152 return -ENOMEM;
153
154 /* Get the tag */
155 ret = bcm63xx_read_image_tag(master, "rootfs", cfelen, buf);
156 if (!ret) {
157 STR_NULL_TERMINATE(buf->flash_image_start);
158 if (kstrtouint(buf->flash_image_start, 10, &rootfsaddr) ||
159 rootfsaddr < BCM963XX_EXTENDED_SIZE) {
160 pr_err("invalid rootfs address: %*ph\n",
161 (int)sizeof(buf->flash_image_start),
162 buf->flash_image_start);
163 goto invalid_tag;
164 }
165
166 STR_NULL_TERMINATE(buf->kernel_address);
167 if (kstrtouint(buf->kernel_address, 10, &kerneladdr) ||
168 kerneladdr < BCM963XX_EXTENDED_SIZE) {
169 pr_err("invalid kernel address: %*ph\n",
170 (int)sizeof(buf->kernel_address),
171 buf->kernel_address);
172 goto invalid_tag;
173 }
174
175 STR_NULL_TERMINATE(buf->kernel_length);
176 if (kstrtouint(buf->kernel_length, 10, &kernellen)) {
177 pr_err("invalid kernel length: %*ph\n",
178 (int)sizeof(buf->kernel_length),
179 buf->kernel_length);
180 goto invalid_tag;
181 }
182
183 STR_NULL_TERMINATE(buf->total_length);
184 if (kstrtouint(buf->total_length, 10, &totallen)) {
185 pr_err("invalid total length: %*ph\n",
186 (int)sizeof(buf->total_length),
187 buf->total_length);
188 goto invalid_tag;
189 }
190
191 kerneladdr = kerneladdr - BCM963XX_EXTENDED_SIZE;
192 rootfsaddr = rootfsaddr - BCM963XX_EXTENDED_SIZE;
193 spareaddr = roundup(totallen, master->erasesize) + cfelen;
194
195 if (rootfsaddr < kerneladdr) {
196 /* default Broadcom layout */
197 rootfslen = kerneladdr - rootfsaddr;
198 rootfs_first = true;
199 } else {
200 /* OpenWrt layout */
201 rootfsaddr = kerneladdr + kernellen;
202 rootfslen = spareaddr - rootfsaddr;
203 }
204 } else if (ret > 0) {
205invalid_tag:
206 kernellen = 0;
207 rootfslen = 0;
208 rootfsaddr = 0;
209 spareaddr = cfelen;
210 } else {
211 goto out;
212 }
213 sparelen = master->size - spareaddr - nvramlen;
214
215 /* Determine number of partitions */
216 if (rootfslen > 0)
217 nrparts++;
218
219 if (kernellen > 0)
220 nrparts++;
221
222 parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL); 118 parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL);
223 if (!parts) { 119 if (!parts)
224 ret = -ENOMEM; 120 return -ENOMEM;
225 goto out;
226 }
227 121
228 /* Start building partition list */ 122 /* Start building partition list */
229 parts[curpart].name = "CFE"; 123 parts[curpart].name = "CFE";
@@ -231,30 +125,6 @@ invalid_tag:
231 parts[curpart].size = cfelen; 125 parts[curpart].size = cfelen;
232 curpart++; 126 curpart++;
233 127
234 if (kernellen > 0) {
235 int kernelpart = curpart;
236
237 if (rootfslen > 0 && rootfs_first)
238 kernelpart++;
239 parts[kernelpart].name = "kernel";
240 parts[kernelpart].offset = kerneladdr;
241 parts[kernelpart].size = kernellen;
242 curpart++;
243 }
244
245 if (rootfslen > 0) {
246 int rootfspart = curpart;
247
248 if (kernellen > 0 && rootfs_first)
249 rootfspart--;
250 parts[rootfspart].name = "rootfs";
251 parts[rootfspart].offset = rootfsaddr;
252 parts[rootfspart].size = rootfslen;
253 if (sparelen > 0 && !rootfs_first)
254 parts[rootfspart].size += sparelen;
255 curpart++;
256 }
257
258 parts[curpart].name = "nvram"; 128 parts[curpart].name = "nvram";
259 parts[curpart].offset = master->size - nvramlen; 129 parts[curpart].offset = master->size - nvramlen;
260 parts[curpart].size = nvramlen; 130 parts[curpart].size = nvramlen;
@@ -264,22 +134,13 @@ invalid_tag:
264 parts[curpart].name = "linux"; 134 parts[curpart].name = "linux";
265 parts[curpart].offset = cfelen; 135 parts[curpart].offset = cfelen;
266 parts[curpart].size = master->size - cfelen - nvramlen; 136 parts[curpart].size = master->size - cfelen - nvramlen;
137 parts[curpart].types = bcm63xx_cfe_part_types;
267 138
268 for (i = 0; i < nrparts; i++) 139 for (i = 0; i < nrparts; i++)
269 pr_info("Partition %d is %s offset %llx and length %llx\n", i, 140 pr_info("Partition %d is %s offset %llx and length %llx\n", i,
270 parts[i].name, parts[i].offset, parts[i].size); 141 parts[i].name, parts[i].offset, parts[i].size);
271 142
272 pr_info("Spare partition is offset %x and length %x\n", spareaddr,
273 sparelen);
274
275 *pparts = parts; 143 *pparts = parts;
276 ret = 0;
277
278out:
279 vfree(buf);
280
281 if (ret)
282 return ret;
283 144
284 return nrparts; 145 return nrparts;
285} 146}
diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig
index fccf1950e92d..c8be3f1507ca 100644
--- a/drivers/mtd/parsers/Kconfig
+++ b/drivers/mtd/parsers/Kconfig
@@ -1,3 +1,14 @@
1config MTD_PARSER_IMAGETAG
2 tristate "Parser for BCM963XX Image Tag format partitions"
3 depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST
4 select CRC32
5 help
6 Image Tag is the firmware header used by broadcom on their xDSL line
7 of devices. It is used to describe the offsets and lengths of kernel
8 and rootfs partitions.
9 This driver adds support for parsing a partition with an Image Tag
10 header and creates up to two partitions, kernel and rootfs.
11
1config MTD_PARSER_TRX 12config MTD_PARSER_TRX
2 tristate "Parser for TRX format partitions" 13 tristate "Parser for TRX format partitions"
3 depends on MTD && (BCM47XX || ARCH_BCM_5301X || COMPILE_TEST) 14 depends on MTD && (BCM47XX || ARCH_BCM_5301X || COMPILE_TEST)
diff --git a/drivers/mtd/parsers/Makefile b/drivers/mtd/parsers/Makefile
index d8418bf6804a..3860c4464c63 100644
--- a/drivers/mtd/parsers/Makefile
+++ b/drivers/mtd/parsers/Makefile
@@ -1,3 +1,4 @@
1obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o
1obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o 2obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
2obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o 3obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
3obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o 4obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
diff --git a/drivers/mtd/parsers/parser_imagetag.c b/drivers/mtd/parsers/parser_imagetag.c
new file mode 100644
index 000000000000..74b66d009b5c
--- /dev/null
+++ b/drivers/mtd/parsers/parser_imagetag.c
@@ -0,0 +1,214 @@
1/*
2 * BCM63XX CFE image tag parser
3 *
4 * Copyright © 2006-2008 Florian Fainelli <florian@openwrt.org>
5 * Mike Albon <malbon@openwrt.org>
6 * Copyright © 2009-2010 Daniel Dickinson <openwrt@cshore.neomailbox.net>
7 * Copyright © 2011-2013 Jonas Gorski <jonas.gorski@gmail.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 */
15
16#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17
18#include <linux/bcm963xx_tag.h>
19#include <linux/crc32.h>
20#include <linux/module.h>
21#include <linux/kernel.h>
22#include <linux/sizes.h>
23#include <linux/slab.h>
24#include <linux/vmalloc.h>
25#include <linux/mtd/mtd.h>
26#include <linux/mtd/partitions.h>
27
28/* Ensure strings read from flash structs are null terminated */
29#define STR_NULL_TERMINATE(x) \
30 do { char *_str = (x); _str[sizeof(x) - 1] = 0; } while (0)
31
32static int bcm963xx_read_imagetag(struct mtd_info *master, const char *name,
33 loff_t tag_offset, struct bcm_tag *buf)
34{
35 int ret;
36 size_t retlen;
37 u32 computed_crc;
38
39 ret = mtd_read(master, tag_offset, sizeof(*buf), &retlen, (void *)buf);
40 if (ret)
41 return ret;
42
43 if (retlen != sizeof(*buf))
44 return -EIO;
45
46 computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)buf,
47 offsetof(struct bcm_tag, header_crc));
48 if (computed_crc == buf->header_crc) {
49 STR_NULL_TERMINATE(buf->board_id);
50 STR_NULL_TERMINATE(buf->tag_version);
51
52 pr_info("%s: CFE image tag found at 0x%llx with version %s, board type %s\n",
53 name, tag_offset, buf->tag_version, buf->board_id);
54
55 return 0;
56 }
57
58 pr_warn("%s: CFE image tag at 0x%llx CRC invalid (expected %08x, actual %08x)\n",
59 name, tag_offset, buf->header_crc, computed_crc);
60 return -EINVAL;
61}
62
63static int bcm963xx_parse_imagetag_partitions(struct mtd_info *master,
64 const struct mtd_partition **pparts,
65 struct mtd_part_parser_data *data)
66{
67 /* CFE, NVRAM and global Linux are always present */
68 int nrparts = 0, curpart = 0;
69 struct bcm_tag *buf = NULL;
70 struct mtd_partition *parts;
71 int ret;
72 unsigned int rootfsaddr, kerneladdr, spareaddr, offset;
73 unsigned int rootfslen, kernellen, sparelen, totallen;
74 int i;
75 bool rootfs_first = false;
76
77 buf = vmalloc(sizeof(struct bcm_tag));
78 if (!buf)
79 return -ENOMEM;
80
81 /* Get the tag */
82 ret = bcm963xx_read_imagetag(master, "rootfs", 0, buf);
83 if (!ret) {
84 STR_NULL_TERMINATE(buf->flash_image_start);
85 if (kstrtouint(buf->flash_image_start, 10, &rootfsaddr) ||
86 rootfsaddr < BCM963XX_EXTENDED_SIZE) {
87 pr_err("invalid rootfs address: %*ph\n",
88 (int)sizeof(buf->flash_image_start),
89 buf->flash_image_start);
90 goto out;
91 }
92
93 STR_NULL_TERMINATE(buf->kernel_address);
94 if (kstrtouint(buf->kernel_address, 10, &kerneladdr) ||
95 kerneladdr < BCM963XX_EXTENDED_SIZE) {
96 pr_err("invalid kernel address: %*ph\n",
97 (int)sizeof(buf->kernel_address),
98 buf->kernel_address);
99 goto out;
100 }
101
102 STR_NULL_TERMINATE(buf->kernel_length);
103 if (kstrtouint(buf->kernel_length, 10, &kernellen)) {
104 pr_err("invalid kernel length: %*ph\n",
105 (int)sizeof(buf->kernel_length),
106 buf->kernel_length);
107 goto out;
108 }
109
110 STR_NULL_TERMINATE(buf->total_length);
111 if (kstrtouint(buf->total_length, 10, &totallen)) {
112 pr_err("invalid total length: %*ph\n",
113 (int)sizeof(buf->total_length),
114 buf->total_length);
115 goto out;
116 }
117
118 /*
119 * Addresses are flash absolute, so convert to partition
120 * relative addresses. Assume either kernel or rootfs will
121 * directly follow the image tag.
122 */
123 if (rootfsaddr < kerneladdr)
124 offset = rootfsaddr - sizeof(struct bcm_tag);
125 else
126 offset = kerneladdr - sizeof(struct bcm_tag);
127
128 kerneladdr = kerneladdr - offset;
129 rootfsaddr = rootfsaddr - offset;
130 spareaddr = roundup(totallen, master->erasesize);
131
132 if (rootfsaddr < kerneladdr) {
133 /* default Broadcom layout */
134 rootfslen = kerneladdr - rootfsaddr;
135 rootfs_first = true;
136 } else {
137 /* OpenWrt layout */
138 rootfsaddr = kerneladdr + kernellen;
139 rootfslen = spareaddr - rootfsaddr;
140 }
141 } else {
142 goto out;
143 }
144 sparelen = master->size - spareaddr;
145
146 /* Determine number of partitions */
147 if (rootfslen > 0)
148 nrparts++;
149
150 if (kernellen > 0)
151 nrparts++;
152
153 parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL);
154 if (!parts) {
155 ret = -ENOMEM;
156 goto out;
157 }
158
159 /* Start building partition list */
160 if (kernellen > 0) {
161 int kernelpart = curpart;
162
163 if (rootfslen > 0 && rootfs_first)
164 kernelpart++;
165 parts[kernelpart].name = "kernel";
166 parts[kernelpart].offset = kerneladdr;
167 parts[kernelpart].size = kernellen;
168 curpart++;
169 }
170
171 if (rootfslen > 0) {
172 int rootfspart = curpart;
173
174 if (kernellen > 0 && rootfs_first)
175 rootfspart--;
176 parts[rootfspart].name = "rootfs";
177 parts[rootfspart].offset = rootfsaddr;
178 parts[rootfspart].size = rootfslen;
179 if (sparelen > 0 && !rootfs_first)
180 parts[rootfspart].size += sparelen;
181 curpart++;
182 }
183
184 for (i = 0; i < nrparts; i++)
185 pr_info("Partition %d is %s offset %llx and length %llx\n", i,
186 parts[i].name, parts[i].offset, parts[i].size);
187
188 pr_info("Spare partition is offset %x and length %x\n", spareaddr,
189 sparelen);
190
191 *pparts = parts;
192 ret = 0;
193
194out:
195 vfree(buf);
196
197 if (ret)
198 return ret;
199
200 return nrparts;
201}
202
203static struct mtd_part_parser bcm963xx_imagetag_parser = {
204 .parse_fn = bcm963xx_parse_imagetag_partitions,
205 .name = "bcm963xx-imagetag",
206};
207module_mtd_part_parser(bcm963xx_imagetag_parser);
208
209MODULE_LICENSE("GPL");
210MODULE_AUTHOR("Daniel Dickinson <openwrt@cshore.neomailbox.net>");
211MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
212MODULE_AUTHOR("Mike Albon <malbon@openwrt.org>");
213MODULE_AUTHOR("Jonas Gorski <jonas.gorski@gmail.com>");
214MODULE_DESCRIPTION("MTD parser for BCM963XX CFE Image Tag partitions");