brintos

brintos / linux-shallow public Read only

0
0
Text · 9.1 KiB · 2611669 Raw
396 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*======================================================================3 4    drivers/mtd/afs.c: ARM Flash Layout/Partitioning5 6    Copyright © 2000 ARM Limited7    Copyright (C) 2019 Linus Walleij8 9 10   This is access code for flashes using ARM's flash partitioning11   standards.12 13======================================================================*/14 15#include <linux/module.h>16#include <linux/types.h>17#include <linux/kernel.h>18#include <linux/slab.h>19#include <linux/string.h>20#include <linux/init.h>21 22#include <linux/mtd/mtd.h>23#include <linux/mtd/map.h>24#include <linux/mtd/partitions.h>25 26#define AFSV1_FOOTER_MAGIC 0xA0FFFF9F27#define AFSV2_FOOTER_MAGIC1 0x464C5348 /* "FLSH" */28#define AFSV2_FOOTER_MAGIC2 0x464F4F54 /* "FOOT" */29 30struct footer_v1 {31	u32 image_info_base;	/* Address of first word of ImageFooter  */32	u32 image_start;	/* Start of area reserved by this footer */33	u32 signature;		/* 'Magic' number proves it's a footer   */34	u32 type;		/* Area type: ARM Image, SIB, customer   */35	u32 checksum;		/* Just this structure                   */36};37 38struct image_info_v1 {39	u32 bootFlags;		/* Boot flags, compression etc.          */40	u32 imageNumber;	/* Unique number, selects for boot etc.  */41	u32 loadAddress;	/* Address program should be loaded to   */42	u32 length;		/* Actual size of image                  */43	u32 address;		/* Image is executed from here           */44	char name[16];		/* Null terminated                       */45	u32 headerBase;		/* Flash Address of any stripped header  */46	u32 header_length;	/* Length of header in memory            */47	u32 headerType;		/* AIF, RLF, s-record etc.               */48	u32 checksum;		/* Image checksum (inc. this struct)     */49};50 51static u32 word_sum(void *words, int num)52{53	u32 *p = words;54	u32 sum = 0;55 56	while (num--)57		sum += *p++;58 59	return sum;60}61 62static u32 word_sum_v2(u32 *p, u32 num)63{64	u32 sum = 0;65	int i;66 67	for (i = 0; i < num; i++) {68		u32 val;69 70		val = p[i];71		if (val > ~sum)72			sum++;73		sum += val;74	}75	return ~sum;76}77 78static bool afs_is_v1(struct mtd_info *mtd, u_int off)79{80	/* The magic is 12 bytes from the end of the erase block */81	u_int ptr = off + mtd->erasesize - 12;82	u32 magic;83	size_t sz;84	int ret;85 86	ret = mtd_read(mtd, ptr, 4, &sz, (u_char *)&magic);87	if (ret < 0) {88		printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",89		       ptr, ret);90		return false;91	}92	if (ret >= 0 && sz != 4)93		return false;94 95	return (magic == AFSV1_FOOTER_MAGIC);96}97 98static bool afs_is_v2(struct mtd_info *mtd, u_int off)99{100	/* The magic is the 8 last bytes of the erase block */101	u_int ptr = off + mtd->erasesize - 8;102	u32 foot[2];103	size_t sz;104	int ret;105 106	ret = mtd_read(mtd, ptr, 8, &sz, (u_char *)foot);107	if (ret < 0) {108		printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",109		       ptr, ret);110		return false;111	}112	if (ret >= 0 && sz != 8)113		return false;114 115	return (foot[0] == AFSV2_FOOTER_MAGIC1 &&116		foot[1] == AFSV2_FOOTER_MAGIC2);117}118 119static int afs_parse_v1_partition(struct mtd_info *mtd,120				  u_int off, struct mtd_partition *part)121{122	struct footer_v1 fs;123	struct image_info_v1 iis;124	u_int mask;125	/*126	 * Static checks cannot see that we bail out if we have an error127	 * reading the footer.128	 */129	u_int iis_ptr;130	u_int img_ptr;131	u_int ptr;132	size_t sz;133	int ret;134	int i;135 136	/*137	 * This is the address mask; we use this to mask off out of138	 * range address bits.139	 */140	mask = mtd->size - 1;141 142	ptr = off + mtd->erasesize - sizeof(fs);143	ret = mtd_read(mtd, ptr, sizeof(fs), &sz, (u_char *)&fs);144	if (ret >= 0 && sz != sizeof(fs))145		ret = -EINVAL;146	if (ret < 0) {147		printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",148		       ptr, ret);149		return ret;150	}151	/*152	 * Check the checksum.153	 */154	if (word_sum(&fs, sizeof(fs) / sizeof(u32)) != 0xffffffff)155		return -EINVAL;156 157	/*158	 * Hide the SIB (System Information Block)159	 */160	if (fs.type == 2)161		return 0;162 163	iis_ptr = fs.image_info_base & mask;164	img_ptr = fs.image_start & mask;165 166	/*167	 * Check the image info base.  This can not168	 * be located after the footer structure.169	 */170	if (iis_ptr >= ptr)171		return 0;172 173	/*174	 * Check the start of this image.  The image175	 * data can not be located after this block.176	 */177	if (img_ptr > off)178		return 0;179 180	/* Read the image info block */181	memset(&iis, 0, sizeof(iis));182	ret = mtd_read(mtd, iis_ptr, sizeof(iis), &sz, (u_char *)&iis);183	if (ret < 0) {184		printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",185		       iis_ptr, ret);186		return -EINVAL;187	}188 189	if (sz != sizeof(iis))190		return -EINVAL;191 192	/*193	 * Validate the name - it must be NUL terminated.194	 */195	for (i = 0; i < sizeof(iis.name); i++)196		if (iis.name[i] == '\0')197			break;198	if (i > sizeof(iis.name))199		return -EINVAL;200 201	part->name = kstrdup(iis.name, GFP_KERNEL);202	if (!part->name)203		return -ENOMEM;204 205	part->size = (iis.length + mtd->erasesize - 1) & ~(mtd->erasesize - 1);206	part->offset = img_ptr;207	part->mask_flags = 0;208 209	printk("  mtd: at 0x%08x, %5lluKiB, %8u, %s\n",210	       img_ptr, part->size / 1024,211	       iis.imageNumber, part->name);212 213	return 0;214}215 216static int afs_parse_v2_partition(struct mtd_info *mtd,217				  u_int off, struct mtd_partition *part)218{219	u_int ptr;220	u32 footer[12];221	u32 imginfo[36];222	char *name;223	u32 version;224	u32 entrypoint;225	u32 attributes;226	u32 region_count;227	u32 block_start;228	u32 block_end;229	u32 crc;230	size_t sz;231	int ret;232	int i;233	int pad = 0;234 235	pr_debug("Parsing v2 partition @%08x-%08x\n",236		 off, off + mtd->erasesize);237 238	/* First read the footer */239	ptr = off + mtd->erasesize - sizeof(footer);240	ret = mtd_read(mtd, ptr, sizeof(footer), &sz, (u_char *)footer);241	if ((ret < 0) || (ret >= 0 && sz != sizeof(footer))) {242		pr_err("AFS: mtd read failed at 0x%x: %d\n",243		       ptr, ret);244		return -EIO;245	}246	name = (char *) &footer[0];247	version = footer[9];248	ptr = off + mtd->erasesize - sizeof(footer) - footer[8];249 250	pr_debug("found image \"%s\", version %08x, info @%08x\n",251		 name, version, ptr);252 253	/* Then read the image information */254	ret = mtd_read(mtd, ptr, sizeof(imginfo), &sz, (u_char *)imginfo);255	if ((ret < 0) || (ret >= 0 && sz != sizeof(imginfo))) {256		pr_err("AFS: mtd read failed at 0x%x: %d\n",257		       ptr, ret);258		return -EIO;259	}260 261	/* 32bit platforms have 4 bytes padding */262	crc = word_sum_v2(&imginfo[1], 34);263	if (!crc) {264		pr_debug("Padding 1 word (4 bytes)\n");265		pad = 1;266	} else {267		/* 64bit platforms have 8 bytes padding */268		crc = word_sum_v2(&imginfo[2], 34);269		if (!crc) {270			pr_debug("Padding 2 words (8 bytes)\n");271			pad = 2;272		}273	}274	if (crc) {275		pr_err("AFS: bad checksum on v2 image info: %08x\n", crc);276		return -EINVAL;277	}278	entrypoint = imginfo[pad];279	attributes = imginfo[pad+1];280	region_count = imginfo[pad+2];281	block_start = imginfo[20];282	block_end = imginfo[21];283 284	pr_debug("image entry=%08x, attr=%08x, regions=%08x, "285		 "bs=%08x, be=%08x\n",286		 entrypoint, attributes, region_count,287		 block_start, block_end);288 289	for (i = 0; i < region_count; i++) {290		u32 region_load_addr = imginfo[pad + 3 + i*4];291		u32 region_size = imginfo[pad + 4 + i*4];292		u32 region_offset = imginfo[pad + 5 + i*4];293		u32 region_start;294		u32 region_end;295 296		pr_debug("  region %d: address: %08x, size: %08x, "297			 "offset: %08x\n",298			 i,299			 region_load_addr,300			 region_size,301			 region_offset);302 303		region_start = off + region_offset;304		region_end = region_start + region_size;305		/* Align partition to end of erase block */306		region_end += (mtd->erasesize - 1);307		region_end &= ~(mtd->erasesize -1);308		pr_debug("   partition start = %08x, partition end = %08x\n",309			 region_start, region_end);310 311		/* Create one partition per region */312		part->name = kstrdup(name, GFP_KERNEL);313		if (!part->name)314			return -ENOMEM;315		part->offset = region_start;316		part->size = region_end - region_start;317		part->mask_flags = 0;318	}319 320	return 0;321}322 323static int parse_afs_partitions(struct mtd_info *mtd,324				const struct mtd_partition **pparts,325				struct mtd_part_parser_data *data)326{327	struct mtd_partition *parts;328	u_int off, sz;329	int ret = 0;330	int i;331 332	/* Count the partitions by looping over all erase blocks */333	for (i = off = sz = 0; off < mtd->size; off += mtd->erasesize) {334		if (afs_is_v1(mtd, off)) {335			sz += sizeof(struct mtd_partition);336			i += 1;337		}338		if (afs_is_v2(mtd, off)) {339			sz += sizeof(struct mtd_partition);340			i += 1;341		}342	}343 344	if (!i)345		return 0;346 347	parts = kzalloc(sz, GFP_KERNEL);348	if (!parts)349		return -ENOMEM;350 351	/*352	 * Identify the partitions353	 */354	for (i = off = 0; off < mtd->size; off += mtd->erasesize) {355		if (afs_is_v1(mtd, off)) {356			ret = afs_parse_v1_partition(mtd, off, &parts[i]);357			if (ret)358				goto out_free_parts;359			i++;360		}361		if (afs_is_v2(mtd, off)) {362			ret = afs_parse_v2_partition(mtd, off, &parts[i]);363			if (ret)364				goto out_free_parts;365			i++;366		}367	}368 369	*pparts = parts;370	return i;371 372out_free_parts:373	while (--i >= 0)374		kfree(parts[i].name);375	kfree(parts);376	*pparts = NULL;377	return ret;378}379 380static const struct of_device_id mtd_parser_afs_of_match_table[] = {381	{ .compatible = "arm,arm-firmware-suite" },382	{},383};384MODULE_DEVICE_TABLE(of, mtd_parser_afs_of_match_table);385 386static struct mtd_part_parser afs_parser = {387	.parse_fn = parse_afs_partitions,388	.name = "afs",389	.of_match_table = mtd_parser_afs_of_match_table,390};391module_mtd_part_parser(afs_parser);392 393MODULE_AUTHOR("ARM Ltd");394MODULE_DESCRIPTION("ARM Firmware Suite partition parser");395MODULE_LICENSE("GPL");396