summaryrefslogtreecommitdiffstats
path: root/drivers/mtd/parsers
diff options
context:
space:
mode:
authorLinus Walleij <linus.walleij@linaro.org>2018-11-10 21:01:25 +0100
committerBoris Brezillon <boris.brezillon@bootlin.com>2018-11-12 11:44:13 +0100
commit43f1fd01a635cb4815f83eccd33ebc620b627a95 (patch)
tree28e2bcafe32a89488c332f914c4b39ad78092cfe /drivers/mtd/parsers
parentf7d6cf6d0c7e2d8035f849c9c4407c48924cbd0f (diff)
downloadlinux-stable-43f1fd01a635cb4815f83eccd33ebc620b627a95.tar.gz
linux-stable-43f1fd01a635cb4815f83eccd33ebc620b627a95.tar.bz2
linux-stable-43f1fd01a635cb4815f83eccd33ebc620b627a95.zip
mtd: Move Redboot partition parser
This moves the Redboot partition parser down to the parsers subdirectory. Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
Diffstat (limited to 'drivers/mtd/parsers')
-rw-r--r--drivers/mtd/parsers/Kconfig50
-rw-r--r--drivers/mtd/parsers/Makefile1
-rw-r--r--drivers/mtd/parsers/redboot.c302
3 files changed, 353 insertions, 0 deletions
diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig
index ee5ab994132f..fccf1950e92d 100644
--- a/drivers/mtd/parsers/Kconfig
+++ b/drivers/mtd/parsers/Kconfig
@@ -14,3 +14,53 @@ config MTD_SHARPSL_PARTS
This provides the read-only FTL logic necessary to read the partition
table from the NAND flash of Sharp SL Series (Zaurus) and the MTD
partition parser using this code.
+
+config MTD_REDBOOT_PARTS
+ tristate "RedBoot partition table parsing"
+ help
+ RedBoot is a ROM monitor and bootloader which deals with multiple
+ 'images' in flash devices by putting a table one of the erase
+ blocks on the device, similar to a partition table, which gives
+ the offsets, lengths and names of all the images stored in the
+ flash.
+
+ If you need code which can detect and parse this table, and register
+ MTD 'partitions' corresponding to each image in the table, enable
+ this option.
+
+ You will still need the parsing functions to be called by the driver
+ for your particular device. It won't happen automatically. The
+ SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for
+ example.
+
+if MTD_REDBOOT_PARTS
+
+config MTD_REDBOOT_DIRECTORY_BLOCK
+ int "Location of RedBoot partition table"
+ default "-1"
+ help
+ This option is the Linux counterpart to the
+ CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK RedBoot compile time
+ option.
+
+ The option specifies which Flash sectors holds the RedBoot
+ partition table. A zero or positive value gives an absolute
+ erase block number. A negative value specifies a number of
+ sectors before the end of the device.
+
+ For example "2" means block number 2, "-1" means the last
+ block and "-2" means the penultimate block.
+
+config MTD_REDBOOT_PARTS_UNALLOCATED
+ bool "Include unallocated flash regions"
+ help
+ If you need to register each unallocated flash region as a MTD
+ 'partition', enable this option.
+
+config MTD_REDBOOT_PARTS_READONLY
+ bool "Force read-only for RedBoot system images"
+ help
+ If you need to force read-only for 'RedBoot', 'RedBoot Config' and
+ 'FIS directory' images, enable this option.
+
+endif # MTD_REDBOOT_PARTS
diff --git a/drivers/mtd/parsers/Makefile b/drivers/mtd/parsers/Makefile
index 5b1bcc3d90d9..d8418bf6804a 100644
--- a/drivers/mtd/parsers/Makefile
+++ b/drivers/mtd/parsers/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
+obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
diff --git a/drivers/mtd/parsers/redboot.c b/drivers/mtd/parsers/redboot.c
new file mode 100644
index 000000000000..7623ac5fc586
--- /dev/null
+++ b/drivers/mtd/parsers/redboot.c
@@ -0,0 +1,302 @@
+/*
+ * Parse RedBoot-style Flash Image System (FIS) tables and
+ * produce a Linux partition array to match.
+ *
+ * Copyright © 2001 Red Hat UK Limited
+ * Copyright © 2001-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * 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
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/module.h>
+
+struct fis_image_desc {
+ unsigned char name[16]; // Null terminated name
+ uint32_t flash_base; // Address within FLASH of image
+ uint32_t mem_base; // Address in memory where it executes
+ uint32_t size; // Length of image
+ uint32_t entry_point; // Execution entry point
+ uint32_t data_length; // Length of actual data
+ unsigned char _pad[256-(16+7*sizeof(uint32_t))];
+ uint32_t desc_cksum; // Checksum over image descriptor
+ uint32_t file_cksum; // Checksum over image data
+};
+
+struct fis_list {
+ struct fis_image_desc *img;
+ struct fis_list *next;
+};
+
+static int directory = CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK;
+module_param(directory, int, 0);
+
+static inline int redboot_checksum(struct fis_image_desc *img)
+{
+ /* RedBoot doesn't actually write the desc_cksum field yet AFAICT */
+ return 1;
+}
+
+static int parse_redboot_partitions(struct mtd_info *master,
+ const struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+{
+ int nrparts = 0;
+ struct fis_image_desc *buf;
+ struct mtd_partition *parts;
+ struct fis_list *fl = NULL, *tmp_fl;
+ int ret, i;
+ size_t retlen;
+ char *names;
+ char *nullname;
+ int namelen = 0;
+ int nulllen = 0;
+ int numslots;
+ unsigned long offset;
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+ static char nullstring[] = "unallocated";
+#endif
+
+ if ( directory < 0 ) {
+ offset = master->size + directory * master->erasesize;
+ while (mtd_block_isbad(master, offset)) {
+ if (!offset) {
+ nogood:
+ printk(KERN_NOTICE "Failed to find a non-bad block to check for RedBoot partition table\n");
+ return -EIO;
+ }
+ offset -= master->erasesize;
+ }
+ } else {
+ offset = directory * master->erasesize;
+ while (mtd_block_isbad(master, offset)) {
+ offset += master->erasesize;
+ if (offset == master->size)
+ goto nogood;
+ }
+ }
+ buf = vmalloc(master->erasesize);
+
+ if (!buf)
+ return -ENOMEM;
+
+ printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n",
+ master->name, offset);
+
+ ret = mtd_read(master, offset, master->erasesize, &retlen,
+ (void *)buf);
+
+ if (ret)
+ goto out;
+
+ if (retlen != master->erasesize) {
+ ret = -EIO;
+ goto out;
+ }
+
+ numslots = (master->erasesize / sizeof(struct fis_image_desc));
+ for (i = 0; i < numslots; i++) {
+ if (!memcmp(buf[i].name, "FIS directory", 14)) {
+ /* This is apparently the FIS directory entry for the
+ * FIS directory itself. The FIS directory size is
+ * one erase block; if the buf[i].size field is
+ * swab32(erasesize) then we know we are looking at
+ * a byte swapped FIS directory - swap all the entries!
+ * (NOTE: this is 'size' not 'data_length'; size is
+ * the full size of the entry.)
+ */
+
+ /* RedBoot can combine the FIS directory and
+ config partitions into a single eraseblock;
+ we assume wrong-endian if either the swapped
+ 'size' matches the eraseblock size precisely,
+ or if the swapped size actually fits in an
+ eraseblock while the unswapped size doesn't. */
+ if (swab32(buf[i].size) == master->erasesize ||
+ (buf[i].size > master->erasesize
+ && swab32(buf[i].size) < master->erasesize)) {
+ int j;
+ /* Update numslots based on actual FIS directory size */
+ numslots = swab32(buf[i].size) / sizeof (struct fis_image_desc);
+ for (j = 0; j < numslots; ++j) {
+
+ /* A single 0xff denotes a deleted entry.
+ * Two of them in a row is the end of the table.
+ */
+ if (buf[j].name[0] == 0xff) {
+ if (buf[j].name[1] == 0xff) {
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ /* The unsigned long fields were written with the
+ * wrong byte sex, name and pad have no byte sex.
+ */
+ swab32s(&buf[j].flash_base);
+ swab32s(&buf[j].mem_base);
+ swab32s(&buf[j].size);
+ swab32s(&buf[j].entry_point);
+ swab32s(&buf[j].data_length);
+ swab32s(&buf[j].desc_cksum);
+ swab32s(&buf[j].file_cksum);
+ }
+ } else if (buf[i].size < master->erasesize) {
+ /* Update numslots based on actual FIS directory size */
+ numslots = buf[i].size / sizeof(struct fis_image_desc);
+ }
+ break;
+ }
+ }
+ if (i == numslots) {
+ /* Didn't find it */
+ printk(KERN_NOTICE "No RedBoot partition table detected in %s\n",
+ master->name);
+ ret = 0;
+ goto out;
+ }
+
+ for (i = 0; i < numslots; i++) {
+ struct fis_list *new_fl, **prev;
+
+ if (buf[i].name[0] == 0xff) {
+ if (buf[i].name[1] == 0xff) {
+ break;
+ } else {
+ continue;
+ }
+ }
+ if (!redboot_checksum(&buf[i]))
+ break;
+
+ new_fl = kmalloc(sizeof(struct fis_list), GFP_KERNEL);
+ namelen += strlen(buf[i].name)+1;
+ if (!new_fl) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ new_fl->img = &buf[i];
+ if (data && data->origin)
+ buf[i].flash_base -= data->origin;
+ else
+ buf[i].flash_base &= master->size-1;
+
+ /* I'm sure the JFFS2 code has done me permanent damage.
+ * I now think the following is _normal_
+ */
+ prev = &fl;
+ while(*prev && (*prev)->img->flash_base < new_fl->img->flash_base)
+ prev = &(*prev)->next;
+ new_fl->next = *prev;
+ *prev = new_fl;
+
+ nrparts++;
+ }
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+ if (fl->img->flash_base) {
+ nrparts++;
+ nulllen = sizeof(nullstring);
+ }
+
+ for (tmp_fl = fl; tmp_fl->next; tmp_fl = tmp_fl->next) {
+ if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize <= tmp_fl->next->img->flash_base) {
+ nrparts++;
+ nulllen = sizeof(nullstring);
+ }
+ }
+#endif
+ parts = kzalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL);
+
+ if (!parts) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ nullname = (char *)&parts[nrparts];
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+ if (nulllen > 0) {
+ strcpy(nullname, nullstring);
+ }
+#endif
+ names = nullname + nulllen;
+
+ i=0;
+
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+ if (fl->img->flash_base) {
+ parts[0].name = nullname;
+ parts[0].size = fl->img->flash_base;
+ parts[0].offset = 0;
+ i++;
+ }
+#endif
+ for ( ; i<nrparts; i++) {
+ parts[i].size = fl->img->size;
+ parts[i].offset = fl->img->flash_base;
+ parts[i].name = names;
+
+ strcpy(names, fl->img->name);
+#ifdef CONFIG_MTD_REDBOOT_PARTS_READONLY
+ if (!memcmp(names, "RedBoot", 8) ||
+ !memcmp(names, "RedBoot config", 15) ||
+ !memcmp(names, "FIS directory", 14)) {
+ parts[i].mask_flags = MTD_WRITEABLE;
+ }
+#endif
+ names += strlen(names)+1;
+
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+ if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) {
+ i++;
+ parts[i].offset = parts[i-1].size + parts[i-1].offset;
+ parts[i].size = fl->next->img->flash_base - parts[i].offset;
+ parts[i].name = nullname;
+ }
+#endif
+ tmp_fl = fl;
+ fl = fl->next;
+ kfree(tmp_fl);
+ }
+ ret = nrparts;
+ *pparts = parts;
+ out:
+ while (fl) {
+ struct fis_list *old = fl;
+ fl = fl->next;
+ kfree(old);
+ }
+ vfree(buf);
+ return ret;
+}
+
+static struct mtd_part_parser redboot_parser = {
+ .parse_fn = parse_redboot_partitions,
+ .name = "RedBoot",
+};
+module_mtd_part_parser(redboot_parser);
+
+/* mtd parsers will request the module by parser name */
+MODULE_ALIAS("RedBoot");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_DESCRIPTION("Parsing code for RedBoot Flash Image System (FIS) tables");