xref: /openbmc/linux/drivers/mtd/parsers/afs.c (revision 7b844cf4)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
22aa3b8e1SLinus Walleij /*======================================================================
32aa3b8e1SLinus Walleij 
42aa3b8e1SLinus Walleij     drivers/mtd/afs.c: ARM Flash Layout/Partitioning
52aa3b8e1SLinus Walleij 
62aa3b8e1SLinus Walleij     Copyright © 2000 ARM Limited
7b7cf5e28SLinus Walleij     Copyright (C) 2019 Linus Walleij
82aa3b8e1SLinus Walleij 
92aa3b8e1SLinus Walleij 
102aa3b8e1SLinus Walleij    This is access code for flashes using ARM's flash partitioning
112aa3b8e1SLinus Walleij    standards.
122aa3b8e1SLinus Walleij 
132aa3b8e1SLinus Walleij ======================================================================*/
142aa3b8e1SLinus Walleij 
152aa3b8e1SLinus Walleij #include <linux/module.h>
162aa3b8e1SLinus Walleij #include <linux/types.h>
172aa3b8e1SLinus Walleij #include <linux/kernel.h>
182aa3b8e1SLinus Walleij #include <linux/slab.h>
192aa3b8e1SLinus Walleij #include <linux/string.h>
202aa3b8e1SLinus Walleij #include <linux/init.h>
212aa3b8e1SLinus Walleij 
222aa3b8e1SLinus Walleij #include <linux/mtd/mtd.h>
232aa3b8e1SLinus Walleij #include <linux/mtd/map.h>
242aa3b8e1SLinus Walleij #include <linux/mtd/partitions.h>
252aa3b8e1SLinus Walleij 
262aa3b8e1SLinus Walleij #define AFSV1_FOOTER_MAGIC 0xA0FFFF9F
27b7cf5e28SLinus Walleij #define AFSV2_FOOTER_MAGIC1 0x464C5348 /* "FLSH" */
28b7cf5e28SLinus Walleij #define AFSV2_FOOTER_MAGIC2 0x464F4F54 /* "FOOT" */
292aa3b8e1SLinus Walleij 
302aa3b8e1SLinus Walleij struct footer_v1 {
312aa3b8e1SLinus Walleij 	u32 image_info_base;	/* Address of first word of ImageFooter  */
322aa3b8e1SLinus Walleij 	u32 image_start;	/* Start of area reserved by this footer */
332aa3b8e1SLinus Walleij 	u32 signature;		/* 'Magic' number proves it's a footer   */
342aa3b8e1SLinus Walleij 	u32 type;		/* Area type: ARM Image, SIB, customer   */
352aa3b8e1SLinus Walleij 	u32 checksum;		/* Just this structure                   */
362aa3b8e1SLinus Walleij };
372aa3b8e1SLinus Walleij 
382aa3b8e1SLinus Walleij struct image_info_v1 {
392aa3b8e1SLinus Walleij 	u32 bootFlags;		/* Boot flags, compression etc.          */
402aa3b8e1SLinus Walleij 	u32 imageNumber;	/* Unique number, selects for boot etc.  */
412aa3b8e1SLinus Walleij 	u32 loadAddress;	/* Address program should be loaded to   */
422aa3b8e1SLinus Walleij 	u32 length;		/* Actual size of image                  */
432aa3b8e1SLinus Walleij 	u32 address;		/* Image is executed from here           */
442aa3b8e1SLinus Walleij 	char name[16];		/* Null terminated                       */
452aa3b8e1SLinus Walleij 	u32 headerBase;		/* Flash Address of any stripped header  */
462aa3b8e1SLinus Walleij 	u32 header_length;	/* Length of header in memory            */
472aa3b8e1SLinus Walleij 	u32 headerType;		/* AIF, RLF, s-record etc.               */
482aa3b8e1SLinus Walleij 	u32 checksum;		/* Image checksum (inc. this struct)     */
492aa3b8e1SLinus Walleij };
502aa3b8e1SLinus Walleij 
word_sum(void * words,int num)512aa3b8e1SLinus Walleij static u32 word_sum(void *words, int num)
522aa3b8e1SLinus Walleij {
532aa3b8e1SLinus Walleij 	u32 *p = words;
542aa3b8e1SLinus Walleij 	u32 sum = 0;
552aa3b8e1SLinus Walleij 
562aa3b8e1SLinus Walleij 	while (num--)
572aa3b8e1SLinus Walleij 		sum += *p++;
582aa3b8e1SLinus Walleij 
592aa3b8e1SLinus Walleij 	return sum;
602aa3b8e1SLinus Walleij }
612aa3b8e1SLinus Walleij 
word_sum_v2(u32 * p,u32 num)62b7cf5e28SLinus Walleij static u32 word_sum_v2(u32 *p, u32 num)
63b7cf5e28SLinus Walleij {
64b7cf5e28SLinus Walleij 	u32 sum = 0;
65b7cf5e28SLinus Walleij 	int i;
66b7cf5e28SLinus Walleij 
67b7cf5e28SLinus Walleij 	for (i = 0; i < num; i++) {
68b7cf5e28SLinus Walleij 		u32 val;
69b7cf5e28SLinus Walleij 
70b7cf5e28SLinus Walleij 		val = p[i];
71b7cf5e28SLinus Walleij 		if (val > ~sum)
72b7cf5e28SLinus Walleij 			sum++;
73b7cf5e28SLinus Walleij 		sum += val;
74b7cf5e28SLinus Walleij 	}
75b7cf5e28SLinus Walleij 	return ~sum;
76b7cf5e28SLinus Walleij }
77b7cf5e28SLinus Walleij 
afs_is_v1(struct mtd_info * mtd,u_int off)7820700171SLinus Walleij static bool afs_is_v1(struct mtd_info *mtd, u_int off)
7920700171SLinus Walleij {
8020700171SLinus Walleij 	/* The magic is 12 bytes from the end of the erase block */
8120700171SLinus Walleij 	u_int ptr = off + mtd->erasesize - 12;
8220700171SLinus Walleij 	u32 magic;
8320700171SLinus Walleij 	size_t sz;
8420700171SLinus Walleij 	int ret;
8520700171SLinus Walleij 
8620700171SLinus Walleij 	ret = mtd_read(mtd, ptr, 4, &sz, (u_char *)&magic);
8720700171SLinus Walleij 	if (ret < 0) {
8820700171SLinus Walleij 		printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
8920700171SLinus Walleij 		       ptr, ret);
9020700171SLinus Walleij 		return false;
9120700171SLinus Walleij 	}
9220700171SLinus Walleij 	if (ret >= 0 && sz != 4)
9320700171SLinus Walleij 		return false;
9420700171SLinus Walleij 
9520700171SLinus Walleij 	return (magic == AFSV1_FOOTER_MAGIC);
9620700171SLinus Walleij }
9720700171SLinus Walleij 
afs_is_v2(struct mtd_info * mtd,u_int off)98b7cf5e28SLinus Walleij static bool afs_is_v2(struct mtd_info *mtd, u_int off)
99b7cf5e28SLinus Walleij {
100b7cf5e28SLinus Walleij 	/* The magic is the 8 last bytes of the erase block */
101b7cf5e28SLinus Walleij 	u_int ptr = off + mtd->erasesize - 8;
102b7cf5e28SLinus Walleij 	u32 foot[2];
103b7cf5e28SLinus Walleij 	size_t sz;
104b7cf5e28SLinus Walleij 	int ret;
105b7cf5e28SLinus Walleij 
106b7cf5e28SLinus Walleij 	ret = mtd_read(mtd, ptr, 8, &sz, (u_char *)foot);
107b7cf5e28SLinus Walleij 	if (ret < 0) {
108b7cf5e28SLinus Walleij 		printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
109b7cf5e28SLinus Walleij 		       ptr, ret);
110b7cf5e28SLinus Walleij 		return false;
111b7cf5e28SLinus Walleij 	}
112b7cf5e28SLinus Walleij 	if (ret >= 0 && sz != 8)
113b7cf5e28SLinus Walleij 		return false;
114b7cf5e28SLinus Walleij 
115b7cf5e28SLinus Walleij 	return (foot[0] == AFSV2_FOOTER_MAGIC1 &&
116b7cf5e28SLinus Walleij 		foot[1] == AFSV2_FOOTER_MAGIC2);
117b7cf5e28SLinus Walleij }
118b7cf5e28SLinus Walleij 
afs_parse_v1_partition(struct mtd_info * mtd,u_int off,struct mtd_partition * part)1194aeb1594SLinus Walleij static int afs_parse_v1_partition(struct mtd_info *mtd,
1204aeb1594SLinus Walleij 				  u_int off, struct mtd_partition *part)
1212aa3b8e1SLinus Walleij {
122ff827b4eSLinus Walleij 	struct footer_v1 fs;
1234aeb1594SLinus Walleij 	struct image_info_v1 iis;
1244aeb1594SLinus Walleij 	u_int mask;
1254aeb1594SLinus Walleij 	/*
1264aeb1594SLinus Walleij 	 * Static checks cannot see that we bail out if we have an error
1274aeb1594SLinus Walleij 	 * reading the footer.
1284aeb1594SLinus Walleij 	 */
1293f649ab7SKees Cook 	u_int iis_ptr;
1303f649ab7SKees Cook 	u_int img_ptr;
131ff827b4eSLinus Walleij 	u_int ptr;
132ff827b4eSLinus Walleij 	size_t sz;
1334aeb1594SLinus Walleij 	int ret;
13432e68beaSLinus Walleij 	int i;
1352aa3b8e1SLinus Walleij 
1362aa3b8e1SLinus Walleij 	/*
1372aa3b8e1SLinus Walleij 	 * This is the address mask; we use this to mask off out of
1382aa3b8e1SLinus Walleij 	 * range address bits.
1392aa3b8e1SLinus Walleij 	 */
1402aa3b8e1SLinus Walleij 	mask = mtd->size - 1;
1412aa3b8e1SLinus Walleij 
142ff827b4eSLinus Walleij 	ptr = off + mtd->erasesize - sizeof(fs);
143ff827b4eSLinus Walleij 	ret = mtd_read(mtd, ptr, sizeof(fs), &sz, (u_char *)&fs);
144ff827b4eSLinus Walleij 	if (ret >= 0 && sz != sizeof(fs))
145ff827b4eSLinus Walleij 		ret = -EINVAL;
146ff827b4eSLinus Walleij 	if (ret < 0) {
147ff827b4eSLinus Walleij 		printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
148ff827b4eSLinus Walleij 		       ptr, ret);
1494aeb1594SLinus Walleij 		return ret;
150ff827b4eSLinus Walleij 	}
151ff827b4eSLinus Walleij 	/*
152ff827b4eSLinus Walleij 	 * Check the checksum.
153ff827b4eSLinus Walleij 	 */
154ff827b4eSLinus Walleij 	if (word_sum(&fs, sizeof(fs) / sizeof(u32)) != 0xffffffff)
155ff827b4eSLinus Walleij 		return -EINVAL;
156ff827b4eSLinus Walleij 
157ff827b4eSLinus Walleij 	/*
158ff827b4eSLinus Walleij 	 * Hide the SIB (System Information Block)
159ff827b4eSLinus Walleij 	 */
160ff827b4eSLinus Walleij 	if (fs.type == 2)
161ff827b4eSLinus Walleij 		return 0;
162ff827b4eSLinus Walleij 
163ff827b4eSLinus Walleij 	iis_ptr = fs.image_info_base & mask;
164ff827b4eSLinus Walleij 	img_ptr = fs.image_start & mask;
165ff827b4eSLinus Walleij 
166ff827b4eSLinus Walleij 	/*
167ff827b4eSLinus Walleij 	 * Check the image info base.  This can not
168ff827b4eSLinus Walleij 	 * be located after the footer structure.
169ff827b4eSLinus Walleij 	 */
170ff827b4eSLinus Walleij 	if (iis_ptr >= ptr)
171ff827b4eSLinus Walleij 		return 0;
172ff827b4eSLinus Walleij 
173ff827b4eSLinus Walleij 	/*
174ff827b4eSLinus Walleij 	 * Check the start of this image.  The image
175ff827b4eSLinus Walleij 	 * data can not be located after this block.
176ff827b4eSLinus Walleij 	 */
177ff827b4eSLinus Walleij 	if (img_ptr > off)
178ff827b4eSLinus Walleij 		return 0;
1794aeb1594SLinus Walleij 
1804aeb1594SLinus Walleij 	/* Read the image info block */
18132e68beaSLinus Walleij 	memset(&iis, 0, sizeof(iis));
18232e68beaSLinus Walleij 	ret = mtd_read(mtd, iis_ptr, sizeof(iis), &sz, (u_char *)&iis);
18332e68beaSLinus Walleij 	if (ret < 0) {
18432e68beaSLinus Walleij 		printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
18532e68beaSLinus Walleij 		       iis_ptr, ret);
18632e68beaSLinus Walleij 		return -EINVAL;
18732e68beaSLinus Walleij 	}
18832e68beaSLinus Walleij 
18932e68beaSLinus Walleij 	if (sz != sizeof(iis))
19032e68beaSLinus Walleij 		return -EINVAL;
19132e68beaSLinus Walleij 
19232e68beaSLinus Walleij 	/*
19332e68beaSLinus Walleij 	 * Validate the name - it must be NUL terminated.
19432e68beaSLinus Walleij 	 */
19532e68beaSLinus Walleij 	for (i = 0; i < sizeof(iis.name); i++)
19632e68beaSLinus Walleij 		if (iis.name[i] == '\0')
19732e68beaSLinus Walleij 			break;
19832e68beaSLinus Walleij 	if (i > sizeof(iis.name))
19932e68beaSLinus Walleij 		return -EINVAL;
2004aeb1594SLinus Walleij 
2014aeb1594SLinus Walleij 	part->name = kstrdup(iis.name, GFP_KERNEL);
2024aeb1594SLinus Walleij 	if (!part->name)
2034aeb1594SLinus Walleij 		return -ENOMEM;
2044aeb1594SLinus Walleij 
2054aeb1594SLinus Walleij 	part->size = (iis.length + mtd->erasesize - 1) & ~(mtd->erasesize - 1);
2064aeb1594SLinus Walleij 	part->offset = img_ptr;
2074aeb1594SLinus Walleij 	part->mask_flags = 0;
2084aeb1594SLinus Walleij 
2094aeb1594SLinus Walleij 	printk("  mtd: at 0x%08x, %5lluKiB, %8u, %s\n",
2104aeb1594SLinus Walleij 	       img_ptr, part->size / 1024,
2114aeb1594SLinus Walleij 	       iis.imageNumber, part->name);
2124aeb1594SLinus Walleij 
2134aeb1594SLinus Walleij 	return 0;
2144aeb1594SLinus Walleij }
2154aeb1594SLinus Walleij 
afs_parse_v2_partition(struct mtd_info * mtd,u_int off,struct mtd_partition * part)216b7cf5e28SLinus Walleij static int afs_parse_v2_partition(struct mtd_info *mtd,
217b7cf5e28SLinus Walleij 				  u_int off, struct mtd_partition *part)
218b7cf5e28SLinus Walleij {
219b7cf5e28SLinus Walleij 	u_int ptr;
220b7cf5e28SLinus Walleij 	u32 footer[12];
221b7cf5e28SLinus Walleij 	u32 imginfo[36];
222b7cf5e28SLinus Walleij 	char *name;
223b7cf5e28SLinus Walleij 	u32 version;
224b7cf5e28SLinus Walleij 	u32 entrypoint;
225b7cf5e28SLinus Walleij 	u32 attributes;
226b7cf5e28SLinus Walleij 	u32 region_count;
227b7cf5e28SLinus Walleij 	u32 block_start;
228b7cf5e28SLinus Walleij 	u32 block_end;
229b7cf5e28SLinus Walleij 	u32 crc;
230b7cf5e28SLinus Walleij 	size_t sz;
231b7cf5e28SLinus Walleij 	int ret;
232b7cf5e28SLinus Walleij 	int i;
233b7cf5e28SLinus Walleij 	int pad = 0;
234b7cf5e28SLinus Walleij 
235b7cf5e28SLinus Walleij 	pr_debug("Parsing v2 partition @%08x-%08x\n",
236b7cf5e28SLinus Walleij 		 off, off + mtd->erasesize);
237b7cf5e28SLinus Walleij 
238b7cf5e28SLinus Walleij 	/* First read the footer */
239b7cf5e28SLinus Walleij 	ptr = off + mtd->erasesize - sizeof(footer);
240b7cf5e28SLinus Walleij 	ret = mtd_read(mtd, ptr, sizeof(footer), &sz, (u_char *)footer);
241b7cf5e28SLinus Walleij 	if ((ret < 0) || (ret >= 0 && sz != sizeof(footer))) {
242b7cf5e28SLinus Walleij 		pr_err("AFS: mtd read failed at 0x%x: %d\n",
243b7cf5e28SLinus Walleij 		       ptr, ret);
244b7cf5e28SLinus Walleij 		return -EIO;
245b7cf5e28SLinus Walleij 	}
246b7cf5e28SLinus Walleij 	name = (char *) &footer[0];
247b7cf5e28SLinus Walleij 	version = footer[9];
248b7cf5e28SLinus Walleij 	ptr = off + mtd->erasesize - sizeof(footer) - footer[8];
249b7cf5e28SLinus Walleij 
250b7cf5e28SLinus Walleij 	pr_debug("found image \"%s\", version %08x, info @%08x\n",
251b7cf5e28SLinus Walleij 		 name, version, ptr);
252b7cf5e28SLinus Walleij 
253b7cf5e28SLinus Walleij 	/* Then read the image information */
254b7cf5e28SLinus Walleij 	ret = mtd_read(mtd, ptr, sizeof(imginfo), &sz, (u_char *)imginfo);
255b7cf5e28SLinus Walleij 	if ((ret < 0) || (ret >= 0 && sz != sizeof(imginfo))) {
256b7cf5e28SLinus Walleij 		pr_err("AFS: mtd read failed at 0x%x: %d\n",
257b7cf5e28SLinus Walleij 		       ptr, ret);
258b7cf5e28SLinus Walleij 		return -EIO;
259b7cf5e28SLinus Walleij 	}
260b7cf5e28SLinus Walleij 
261b7cf5e28SLinus Walleij 	/* 32bit platforms have 4 bytes padding */
262b7cf5e28SLinus Walleij 	crc = word_sum_v2(&imginfo[1], 34);
263b7cf5e28SLinus Walleij 	if (!crc) {
264b7cf5e28SLinus Walleij 		pr_debug("Padding 1 word (4 bytes)\n");
265b7cf5e28SLinus Walleij 		pad = 1;
266b7cf5e28SLinus Walleij 	} else {
267b7cf5e28SLinus Walleij 		/* 64bit platforms have 8 bytes padding */
268b7cf5e28SLinus Walleij 		crc = word_sum_v2(&imginfo[2], 34);
269b7cf5e28SLinus Walleij 		if (!crc) {
270b7cf5e28SLinus Walleij 			pr_debug("Padding 2 words (8 bytes)\n");
271b7cf5e28SLinus Walleij 			pad = 2;
272b7cf5e28SLinus Walleij 		}
273b7cf5e28SLinus Walleij 	}
274b7cf5e28SLinus Walleij 	if (crc) {
275b7cf5e28SLinus Walleij 		pr_err("AFS: bad checksum on v2 image info: %08x\n", crc);
276b7cf5e28SLinus Walleij 		return -EINVAL;
277b7cf5e28SLinus Walleij 	}
278b7cf5e28SLinus Walleij 	entrypoint = imginfo[pad];
279b7cf5e28SLinus Walleij 	attributes = imginfo[pad+1];
280b7cf5e28SLinus Walleij 	region_count = imginfo[pad+2];
281b7cf5e28SLinus Walleij 	block_start = imginfo[20];
282b7cf5e28SLinus Walleij 	block_end = imginfo[21];
283b7cf5e28SLinus Walleij 
284b7cf5e28SLinus Walleij 	pr_debug("image entry=%08x, attr=%08x, regions=%08x, "
285b7cf5e28SLinus Walleij 		 "bs=%08x, be=%08x\n",
286b7cf5e28SLinus Walleij 		 entrypoint, attributes, region_count,
287b7cf5e28SLinus Walleij 		 block_start, block_end);
288b7cf5e28SLinus Walleij 
289b7cf5e28SLinus Walleij 	for (i = 0; i < region_count; i++) {
290b7cf5e28SLinus Walleij 		u32 region_load_addr = imginfo[pad + 3 + i*4];
291b7cf5e28SLinus Walleij 		u32 region_size = imginfo[pad + 4 + i*4];
292b7cf5e28SLinus Walleij 		u32 region_offset = imginfo[pad + 5 + i*4];
293b7cf5e28SLinus Walleij 		u32 region_start;
294b7cf5e28SLinus Walleij 		u32 region_end;
295b7cf5e28SLinus Walleij 
296b7cf5e28SLinus Walleij 		pr_debug("  region %d: address: %08x, size: %08x, "
297b7cf5e28SLinus Walleij 			 "offset: %08x\n",
298b7cf5e28SLinus Walleij 			 i,
299b7cf5e28SLinus Walleij 			 region_load_addr,
300b7cf5e28SLinus Walleij 			 region_size,
301b7cf5e28SLinus Walleij 			 region_offset);
302b7cf5e28SLinus Walleij 
303b7cf5e28SLinus Walleij 		region_start = off + region_offset;
304b7cf5e28SLinus Walleij 		region_end = region_start + region_size;
305b7cf5e28SLinus Walleij 		/* Align partition to end of erase block */
306b7cf5e28SLinus Walleij 		region_end += (mtd->erasesize - 1);
307b7cf5e28SLinus Walleij 		region_end &= ~(mtd->erasesize -1);
308b7cf5e28SLinus Walleij 		pr_debug("   partition start = %08x, partition end = %08x\n",
309b7cf5e28SLinus Walleij 			 region_start, region_end);
310b7cf5e28SLinus Walleij 
311b7cf5e28SLinus Walleij 		/* Create one partition per region */
312b7cf5e28SLinus Walleij 		part->name = kstrdup(name, GFP_KERNEL);
313b7cf5e28SLinus Walleij 		if (!part->name)
314b7cf5e28SLinus Walleij 			return -ENOMEM;
315b7cf5e28SLinus Walleij 		part->offset = region_start;
316b7cf5e28SLinus Walleij 		part->size = region_end - region_start;
317b7cf5e28SLinus Walleij 		part->mask_flags = 0;
318b7cf5e28SLinus Walleij 	}
319b7cf5e28SLinus Walleij 
320b7cf5e28SLinus Walleij 	return 0;
321b7cf5e28SLinus Walleij }
322b7cf5e28SLinus Walleij 
parse_afs_partitions(struct mtd_info * mtd,const struct mtd_partition ** pparts,struct mtd_part_parser_data * data)3234aeb1594SLinus Walleij static int parse_afs_partitions(struct mtd_info *mtd,
3244aeb1594SLinus Walleij 				const struct mtd_partition **pparts,
3254aeb1594SLinus Walleij 				struct mtd_part_parser_data *data)
3264aeb1594SLinus Walleij {
3274aeb1594SLinus Walleij 	struct mtd_partition *parts;
3284aeb1594SLinus Walleij 	u_int off, sz;
3294aeb1594SLinus Walleij 	int ret = 0;
3304aeb1594SLinus Walleij 	int i;
3314aeb1594SLinus Walleij 
33220700171SLinus Walleij 	/* Count the partitions by looping over all erase blocks */
3331fca1f6aSLinus Walleij 	for (i = off = sz = 0; off < mtd->size; off += mtd->erasesize) {
33420700171SLinus Walleij 		if (afs_is_v1(mtd, off)) {
3351fca1f6aSLinus Walleij 			sz += sizeof(struct mtd_partition);
3361fca1f6aSLinus Walleij 			i += 1;
3371fca1f6aSLinus Walleij 		}
338b7cf5e28SLinus Walleij 		if (afs_is_v2(mtd, off)) {
339b7cf5e28SLinus Walleij 			sz += sizeof(struct mtd_partition);
340b7cf5e28SLinus Walleij 			i += 1;
341b7cf5e28SLinus Walleij 		}
3421fca1f6aSLinus Walleij 	}
3431fca1f6aSLinus Walleij 
3441fca1f6aSLinus Walleij 	if (!i)
3451fca1f6aSLinus Walleij 		return 0;
3462aa3b8e1SLinus Walleij 
3472aa3b8e1SLinus Walleij 	parts = kzalloc(sz, GFP_KERNEL);
3482aa3b8e1SLinus Walleij 	if (!parts)
3492aa3b8e1SLinus Walleij 		return -ENOMEM;
3502aa3b8e1SLinus Walleij 
3512aa3b8e1SLinus Walleij 	/*
3522aa3b8e1SLinus Walleij 	 * Identify the partitions
3532aa3b8e1SLinus Walleij 	 */
3541fca1f6aSLinus Walleij 	for (i = off = 0; off < mtd->size; off += mtd->erasesize) {
3554aeb1594SLinus Walleij 		if (afs_is_v1(mtd, off)) {
3564aeb1594SLinus Walleij 			ret = afs_parse_v1_partition(mtd, off, &parts[i]);
3574aeb1594SLinus Walleij 			if (ret)
3581fca1f6aSLinus Walleij 				goto out_free_parts;
3594aeb1594SLinus Walleij 			i++;
3602aa3b8e1SLinus Walleij 		}
361b7cf5e28SLinus Walleij 		if (afs_is_v2(mtd, off)) {
362b7cf5e28SLinus Walleij 			ret = afs_parse_v2_partition(mtd, off, &parts[i]);
363b7cf5e28SLinus Walleij 			if (ret)
364b7cf5e28SLinus Walleij 				goto out_free_parts;
365b7cf5e28SLinus Walleij 			i++;
366b7cf5e28SLinus Walleij 		}
3672aa3b8e1SLinus Walleij 	}
3682aa3b8e1SLinus Walleij 
3692aa3b8e1SLinus Walleij 	*pparts = parts;
3701fca1f6aSLinus Walleij 	return i;
3711fca1f6aSLinus Walleij 
3721fca1f6aSLinus Walleij out_free_parts:
373*7b844cf4SManivannan Sadhasivam 	while (--i >= 0)
3741fca1f6aSLinus Walleij 		kfree(parts[i].name);
3751fca1f6aSLinus Walleij 	kfree(parts);
3761fca1f6aSLinus Walleij 	*pparts = NULL;
3771fca1f6aSLinus Walleij 	return ret;
3782aa3b8e1SLinus Walleij }
3792aa3b8e1SLinus Walleij 
38022749bf5SLinus Walleij static const struct of_device_id mtd_parser_afs_of_match_table[] = {
38122749bf5SLinus Walleij 	{ .compatible = "arm,arm-firmware-suite" },
38222749bf5SLinus Walleij 	{},
38322749bf5SLinus Walleij };
38422749bf5SLinus Walleij MODULE_DEVICE_TABLE(of, mtd_parser_afs_of_match_table);
38522749bf5SLinus Walleij 
3862aa3b8e1SLinus Walleij static struct mtd_part_parser afs_parser = {
3872aa3b8e1SLinus Walleij 	.parse_fn = parse_afs_partitions,
3882aa3b8e1SLinus Walleij 	.name = "afs",
38922749bf5SLinus Walleij 	.of_match_table = mtd_parser_afs_of_match_table,
3902aa3b8e1SLinus Walleij };
3912aa3b8e1SLinus Walleij module_mtd_part_parser(afs_parser);
3922aa3b8e1SLinus Walleij 
3932aa3b8e1SLinus Walleij MODULE_AUTHOR("ARM Ltd");
3942aa3b8e1SLinus Walleij MODULE_DESCRIPTION("ARM Firmware Suite partition parser");
3952aa3b8e1SLinus Walleij MODULE_LICENSE("GPL");
396