brintos

brintos / linux-shallow public Read only

0
0
Text · 8.2 KiB · 3b55b67 Raw
324 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Parse RedBoot-style Flash Image System (FIS) tables and4 * produce a Linux partition array to match.5 *6 * Copyright © 2001      Red Hat UK Limited7 * Copyright © 2001-2010 David Woodhouse <dwmw2@infradead.org>8 */9 10#include <linux/kernel.h>11#include <linux/slab.h>12#include <linux/init.h>13#include <linux/vmalloc.h>14#include <linux/of.h>15#include <linux/mtd/mtd.h>16#include <linux/mtd/partitions.h>17#include <linux/module.h>18 19struct fis_image_desc {20	unsigned char name[16];      // Null terminated name21	u32	  flash_base;    // Address within FLASH of image22	u32	  mem_base;      // Address in memory where it executes23	u32	  size;          // Length of image24	u32	  entry_point;   // Execution entry point25	u32	  data_length;   // Length of actual data26	unsigned char _pad[256 - (16 + 7 * sizeof(u32))];27	u32	  desc_cksum;    // Checksum over image descriptor28	u32	  file_cksum;    // Checksum over image data29};30 31struct fis_list {32	struct fis_image_desc *img;33	struct fis_list *next;34};35 36static int directory = CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK;37module_param(directory, int, 0);38 39static inline int redboot_checksum(struct fis_image_desc *img)40{41	/* RedBoot doesn't actually write the desc_cksum field yet AFAICT */42	return 1;43}44 45static void parse_redboot_of(struct mtd_info *master)46{47	struct device_node *np;48	struct device_node *npart;49	u32 dirblock;50	int ret;51 52	np = mtd_get_of_node(master);53	if (!np)54		return;55 56	npart = of_get_child_by_name(np, "partitions");57	if (!npart)58		return;59 60	ret = of_property_read_u32(npart, "fis-index-block", &dirblock);61	of_node_put(npart);62	if (ret)63		return;64 65	/*66	 * Assign the block found in the device tree to the local67	 * directory block pointer.68	 */69	directory = dirblock;70}71 72static int parse_redboot_partitions(struct mtd_info *master,73				    const struct mtd_partition **pparts,74				    struct mtd_part_parser_data *data)75{76	int nrparts = 0;77	struct fis_image_desc *buf;78	struct mtd_partition *parts;79	struct fis_list *fl = NULL, *tmp_fl;80	int ret, i;81	size_t retlen;82	char *names;83	char *nullname;84	int namelen = 0;85	int nulllen = 0;86	int numslots;87	unsigned long offset;88#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED89	static char nullstring[] = "unallocated";90#endif91 92	parse_redboot_of(master);93 94	if (directory < 0) {95		offset = master->size + directory * master->erasesize;96		while (mtd_block_isbad(master, offset)) {97			if (!offset) {98nogood:99				pr_notice("Failed to find a non-bad block to check for RedBoot partition table\n");100				return -EIO;101			}102			offset -= master->erasesize;103		}104	} else {105		offset = (unsigned long) directory * master->erasesize;106		while (mtd_block_isbad(master, offset)) {107			offset += master->erasesize;108			if (offset == master->size)109				goto nogood;110		}111	}112	buf = vmalloc(master->erasesize);113 114	if (!buf)115		return -ENOMEM;116 117	pr_notice("Searching for RedBoot partition table in %s at offset 0x%lx\n",118		  master->name, offset);119 120	ret = mtd_read(master, offset, master->erasesize, &retlen,121		       (void *)buf);122 123	if (ret)124		goto out;125 126	if (retlen != master->erasesize) {127		ret = -EIO;128		goto out;129	}130 131	numslots = (master->erasesize / sizeof(struct fis_image_desc));132	for (i = 0; i < numslots; i++) {133		if (!memcmp(buf[i].name, "FIS directory", 14)) {134			/* This is apparently the FIS directory entry for the135			 * FIS directory itself.  The FIS directory size is136			 * one erase block; if the buf[i].size field is137			 * swab32(erasesize) then we know we are looking at138			 * a byte swapped FIS directory - swap all the entries!139			 * (NOTE: this is 'size' not 'data_length'; size is140			 * the full size of the entry.)141			 */142 143			/* RedBoot can combine the FIS directory and144			   config partitions into a single eraseblock;145			   we assume wrong-endian if either the swapped146			   'size' matches the eraseblock size precisely,147			   or if the swapped size actually fits in an148			   eraseblock while the unswapped size doesn't. */149			if (swab32(buf[i].size) == master->erasesize ||150			    (buf[i].size > master->erasesize151			     && swab32(buf[i].size) < master->erasesize)) {152				int j;153				/* Update numslots based on actual FIS directory size */154				numslots = swab32(buf[i].size) / sizeof(struct fis_image_desc);155				for (j = 0; j < numslots; ++j) {156					/* A single 0xff denotes a deleted entry.157					 * Two of them in a row is the end of the table.158					 */159					if (buf[j].name[0] == 0xff) {160						if (buf[j].name[1] == 0xff) {161							break;162						} else {163							continue;164						}165					}166 167					/* The unsigned long fields were written with the168					 * wrong byte sex, name and pad have no byte sex.169					 */170					swab32s(&buf[j].flash_base);171					swab32s(&buf[j].mem_base);172					swab32s(&buf[j].size);173					swab32s(&buf[j].entry_point);174					swab32s(&buf[j].data_length);175					swab32s(&buf[j].desc_cksum);176					swab32s(&buf[j].file_cksum);177				}178			} else if (buf[i].size < master->erasesize) {179				/* Update numslots based on actual FIS directory size */180				numslots = buf[i].size / sizeof(struct fis_image_desc);181			}182			break;183		}184	}185	if (i == numslots) {186		/* Didn't find it */187		pr_notice("No RedBoot partition table detected in %s\n",188			  master->name);189		ret = 0;190		goto out;191	}192 193	for (i = 0; i < numslots; i++) {194		struct fis_list *new_fl, **prev;195 196		if (buf[i].name[0] == 0xff) {197			if (buf[i].name[1] == 0xff) {198				break;199			} else {200				continue;201			}202		}203		if (!redboot_checksum(&buf[i]))204			break;205 206		new_fl = kmalloc(sizeof(struct fis_list), GFP_KERNEL);207		namelen += strlen(buf[i].name) + 1;208		if (!new_fl) {209			ret = -ENOMEM;210			goto out;211		}212		new_fl->img = &buf[i];213		if (data && data->origin)214			buf[i].flash_base -= data->origin;215		else216			buf[i].flash_base &= master->size - 1;217 218		/* I'm sure the JFFS2 code has done me permanent damage.219		 * I now think the following is _normal_220		 */221		prev = &fl;222		while (*prev && (*prev)->img->flash_base < new_fl->img->flash_base)223			prev = &(*prev)->next;224		new_fl->next = *prev;225		*prev = new_fl;226 227		nrparts++;228	}229#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED230	if (fl->img->flash_base) {231		nrparts++;232		nulllen = sizeof(nullstring);233	}234 235	for (tmp_fl = fl; tmp_fl->next; tmp_fl = tmp_fl->next) {236		if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize <= tmp_fl->next->img->flash_base) {237			nrparts++;238			nulllen = sizeof(nullstring);239		}240	}241#endif242	parts = kzalloc(sizeof(*parts) * nrparts + nulllen + namelen, GFP_KERNEL);243 244	if (!parts) {245		ret = -ENOMEM;246		goto out;247	}248 249	nullname = (char *)&parts[nrparts];250#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED251	if (nulllen > 0)252		strcpy(nullname, nullstring);253#endif254	names = nullname + nulllen;255 256	i = 0;257 258#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED259	if (fl->img->flash_base) {260		parts[0].name = nullname;261		parts[0].size = fl->img->flash_base;262		parts[0].offset = 0;263		i++;264	}265#endif266	for ( ; i < nrparts; i++) {267		parts[i].size = fl->img->size;268		parts[i].offset = fl->img->flash_base;269		parts[i].name = names;270 271		strcpy(names, fl->img->name);272#ifdef CONFIG_MTD_REDBOOT_PARTS_READONLY273		if (!memcmp(names, "RedBoot", 8) ||274		    !memcmp(names, "RedBoot config", 15) ||275		    !memcmp(names, "FIS directory", 14)) {276			parts[i].mask_flags = MTD_WRITEABLE;277		}278#endif279		names += strlen(names) + 1;280 281#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED282		if (fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) {283			i++;284			parts[i].offset = parts[i - 1].size + parts[i - 1].offset;285			parts[i].size = fl->next->img->flash_base - parts[i].offset;286			parts[i].name = nullname;287		}288#endif289		tmp_fl = fl;290		fl = fl->next;291		kfree(tmp_fl);292	}293	ret = nrparts;294	*pparts = parts;295 out:296	while (fl) {297		struct fis_list *old = fl;298 299		fl = fl->next;300		kfree(old);301	}302	vfree(buf);303	return ret;304}305 306static const struct of_device_id mtd_parser_redboot_of_match_table[] = {307	{ .compatible = "redboot-fis" },308	{},309};310MODULE_DEVICE_TABLE(of, mtd_parser_redboot_of_match_table);311 312static struct mtd_part_parser redboot_parser = {313	.parse_fn = parse_redboot_partitions,314	.name = "RedBoot",315	.of_match_table = mtd_parser_redboot_of_match_table,316};317module_mtd_part_parser(redboot_parser);318 319/* mtd parsers will request the module by parser name */320MODULE_ALIAS("RedBoot");321MODULE_LICENSE("GPL");322MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");323MODULE_DESCRIPTION("Parsing code for RedBoot Flash Image System (FIS) tables");324