xref: /openbmc/linux/drivers/mtd/parsers/afs.c (revision ff827b4e)
12aa3b8e1SLinus Walleij /*======================================================================
22aa3b8e1SLinus Walleij 
32aa3b8e1SLinus Walleij     drivers/mtd/afs.c: ARM Flash Layout/Partitioning
42aa3b8e1SLinus Walleij 
52aa3b8e1SLinus Walleij     Copyright © 2000 ARM Limited
62aa3b8e1SLinus Walleij 
72aa3b8e1SLinus Walleij    This program is free software; you can redistribute it and/or modify
82aa3b8e1SLinus Walleij    it under the terms of the GNU General Public License as published by
92aa3b8e1SLinus Walleij    the Free Software Foundation; either version 2 of the License, or
102aa3b8e1SLinus Walleij    (at your option) any later version.
112aa3b8e1SLinus Walleij 
122aa3b8e1SLinus Walleij    This program is distributed in the hope that it will be useful,
132aa3b8e1SLinus Walleij    but WITHOUT ANY WARRANTY; without even the implied warranty of
142aa3b8e1SLinus Walleij    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
152aa3b8e1SLinus Walleij    GNU General Public License for more details.
162aa3b8e1SLinus Walleij 
172aa3b8e1SLinus Walleij    You should have received a copy of the GNU General Public License
182aa3b8e1SLinus Walleij    along with this program; if not, write to the Free Software
192aa3b8e1SLinus Walleij    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
202aa3b8e1SLinus Walleij 
212aa3b8e1SLinus Walleij    This is access code for flashes using ARM's flash partitioning
222aa3b8e1SLinus Walleij    standards.
232aa3b8e1SLinus Walleij 
242aa3b8e1SLinus Walleij ======================================================================*/
252aa3b8e1SLinus Walleij 
262aa3b8e1SLinus Walleij #include <linux/module.h>
272aa3b8e1SLinus Walleij #include <linux/types.h>
282aa3b8e1SLinus Walleij #include <linux/kernel.h>
292aa3b8e1SLinus Walleij #include <linux/slab.h>
302aa3b8e1SLinus Walleij #include <linux/string.h>
312aa3b8e1SLinus Walleij #include <linux/init.h>
322aa3b8e1SLinus Walleij 
332aa3b8e1SLinus Walleij #include <linux/mtd/mtd.h>
342aa3b8e1SLinus Walleij #include <linux/mtd/map.h>
352aa3b8e1SLinus Walleij #include <linux/mtd/partitions.h>
362aa3b8e1SLinus Walleij 
372aa3b8e1SLinus Walleij #define AFSV1_FOOTER_MAGIC 0xA0FFFF9F
382aa3b8e1SLinus Walleij 
392aa3b8e1SLinus Walleij struct footer_v1 {
402aa3b8e1SLinus Walleij 	u32 image_info_base;	/* Address of first word of ImageFooter  */
412aa3b8e1SLinus Walleij 	u32 image_start;	/* Start of area reserved by this footer */
422aa3b8e1SLinus Walleij 	u32 signature;		/* 'Magic' number proves it's a footer   */
432aa3b8e1SLinus Walleij 	u32 type;		/* Area type: ARM Image, SIB, customer   */
442aa3b8e1SLinus Walleij 	u32 checksum;		/* Just this structure                   */
452aa3b8e1SLinus Walleij };
462aa3b8e1SLinus Walleij 
472aa3b8e1SLinus Walleij struct image_info_v1 {
482aa3b8e1SLinus Walleij 	u32 bootFlags;		/* Boot flags, compression etc.          */
492aa3b8e1SLinus Walleij 	u32 imageNumber;	/* Unique number, selects for boot etc.  */
502aa3b8e1SLinus Walleij 	u32 loadAddress;	/* Address program should be loaded to   */
512aa3b8e1SLinus Walleij 	u32 length;		/* Actual size of image                  */
522aa3b8e1SLinus Walleij 	u32 address;		/* Image is executed from here           */
532aa3b8e1SLinus Walleij 	char name[16];		/* Null terminated                       */
542aa3b8e1SLinus Walleij 	u32 headerBase;		/* Flash Address of any stripped header  */
552aa3b8e1SLinus Walleij 	u32 header_length;	/* Length of header in memory            */
562aa3b8e1SLinus Walleij 	u32 headerType;		/* AIF, RLF, s-record etc.               */
572aa3b8e1SLinus Walleij 	u32 checksum;		/* Image checksum (inc. this struct)     */
582aa3b8e1SLinus Walleij };
592aa3b8e1SLinus Walleij 
602aa3b8e1SLinus Walleij static u32 word_sum(void *words, int num)
612aa3b8e1SLinus Walleij {
622aa3b8e1SLinus Walleij 	u32 *p = words;
632aa3b8e1SLinus Walleij 	u32 sum = 0;
642aa3b8e1SLinus Walleij 
652aa3b8e1SLinus Walleij 	while (num--)
662aa3b8e1SLinus Walleij 		sum += *p++;
672aa3b8e1SLinus Walleij 
682aa3b8e1SLinus Walleij 	return sum;
692aa3b8e1SLinus Walleij }
702aa3b8e1SLinus Walleij 
7120700171SLinus Walleij static bool afs_is_v1(struct mtd_info *mtd, u_int off)
7220700171SLinus Walleij {
7320700171SLinus Walleij 	/* The magic is 12 bytes from the end of the erase block */
7420700171SLinus Walleij 	u_int ptr = off + mtd->erasesize - 12;
7520700171SLinus Walleij 	u32 magic;
7620700171SLinus Walleij 	size_t sz;
7720700171SLinus Walleij 	int ret;
7820700171SLinus Walleij 
7920700171SLinus Walleij 	ret = mtd_read(mtd, ptr, 4, &sz, (u_char *)&magic);
8020700171SLinus Walleij 	if (ret < 0) {
8120700171SLinus Walleij 		printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
8220700171SLinus Walleij 		       ptr, ret);
8320700171SLinus Walleij 		return false;
8420700171SLinus Walleij 	}
8520700171SLinus Walleij 	if (ret >= 0 && sz != 4)
8620700171SLinus Walleij 		return false;
8720700171SLinus Walleij 
8820700171SLinus Walleij 	return (magic == AFSV1_FOOTER_MAGIC);
8920700171SLinus Walleij }
9020700171SLinus Walleij 
912aa3b8e1SLinus Walleij static int
922aa3b8e1SLinus Walleij afs_read_iis_v1(struct mtd_info *mtd, struct image_info_v1 *iis, u_int ptr)
932aa3b8e1SLinus Walleij {
942aa3b8e1SLinus Walleij 	size_t sz;
952aa3b8e1SLinus Walleij 	int ret, i;
962aa3b8e1SLinus Walleij 
972aa3b8e1SLinus Walleij 	memset(iis, 0, sizeof(*iis));
982aa3b8e1SLinus Walleij 	ret = mtd_read(mtd, ptr, sizeof(*iis), &sz, (u_char *)iis);
992aa3b8e1SLinus Walleij 	if (ret < 0)
1002aa3b8e1SLinus Walleij 		goto failed;
1012aa3b8e1SLinus Walleij 
1022aa3b8e1SLinus Walleij 	if (sz != sizeof(*iis)) {
1032aa3b8e1SLinus Walleij 		ret = -EINVAL;
1042aa3b8e1SLinus Walleij 		goto failed;
1052aa3b8e1SLinus Walleij 	}
1062aa3b8e1SLinus Walleij 
1072aa3b8e1SLinus Walleij 	ret = 0;
1082aa3b8e1SLinus Walleij 
1092aa3b8e1SLinus Walleij 	/*
1102aa3b8e1SLinus Walleij 	 * Validate the name - it must be NUL terminated.
1112aa3b8e1SLinus Walleij 	 */
1122aa3b8e1SLinus Walleij 	for (i = 0; i < sizeof(iis->name); i++)
1132aa3b8e1SLinus Walleij 		if (iis->name[i] == '\0')
1142aa3b8e1SLinus Walleij 			break;
1152aa3b8e1SLinus Walleij 
1162aa3b8e1SLinus Walleij 	if (i < sizeof(iis->name))
1172aa3b8e1SLinus Walleij 		ret = 1;
1182aa3b8e1SLinus Walleij 
1192aa3b8e1SLinus Walleij 	return ret;
1202aa3b8e1SLinus Walleij 
1212aa3b8e1SLinus Walleij  failed:
1222aa3b8e1SLinus Walleij 	printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
1232aa3b8e1SLinus Walleij 		ptr, ret);
1242aa3b8e1SLinus Walleij 	return ret;
1252aa3b8e1SLinus Walleij }
1262aa3b8e1SLinus Walleij 
1274aeb1594SLinus Walleij static int afs_parse_v1_partition(struct mtd_info *mtd,
1284aeb1594SLinus Walleij 				  u_int off, struct mtd_partition *part)
1292aa3b8e1SLinus Walleij {
130ff827b4eSLinus Walleij 	struct footer_v1 fs;
1314aeb1594SLinus Walleij 	struct image_info_v1 iis;
1324aeb1594SLinus Walleij 	u_int mask;
1334aeb1594SLinus Walleij 	/*
1344aeb1594SLinus Walleij 	 * Static checks cannot see that we bail out if we have an error
1354aeb1594SLinus Walleij 	 * reading the footer.
1364aeb1594SLinus Walleij 	 */
1374aeb1594SLinus Walleij 	u_int uninitialized_var(iis_ptr);
1384aeb1594SLinus Walleij 	u_int uninitialized_var(img_ptr);
139ff827b4eSLinus Walleij 	u_int ptr;
140ff827b4eSLinus Walleij 	size_t sz;
1414aeb1594SLinus Walleij 	int ret;
1422aa3b8e1SLinus Walleij 
1432aa3b8e1SLinus Walleij 	/*
1442aa3b8e1SLinus Walleij 	 * This is the address mask; we use this to mask off out of
1452aa3b8e1SLinus Walleij 	 * range address bits.
1462aa3b8e1SLinus Walleij 	 */
1472aa3b8e1SLinus Walleij 	mask = mtd->size - 1;
1482aa3b8e1SLinus Walleij 
149ff827b4eSLinus Walleij 	ptr = off + mtd->erasesize - sizeof(fs);
150ff827b4eSLinus Walleij 	ret = mtd_read(mtd, ptr, sizeof(fs), &sz, (u_char *)&fs);
151ff827b4eSLinus Walleij 	if (ret >= 0 && sz != sizeof(fs))
152ff827b4eSLinus Walleij 		ret = -EINVAL;
153ff827b4eSLinus Walleij 	if (ret < 0) {
154ff827b4eSLinus Walleij 		printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
155ff827b4eSLinus Walleij 		       ptr, ret);
1564aeb1594SLinus Walleij 		return ret;
157ff827b4eSLinus Walleij 	}
158ff827b4eSLinus Walleij 	/*
159ff827b4eSLinus Walleij 	 * Check the checksum.
160ff827b4eSLinus Walleij 	 */
161ff827b4eSLinus Walleij 	if (word_sum(&fs, sizeof(fs) / sizeof(u32)) != 0xffffffff)
162ff827b4eSLinus Walleij 		return -EINVAL;
163ff827b4eSLinus Walleij 
164ff827b4eSLinus Walleij 	/*
165ff827b4eSLinus Walleij 	 * Hide the SIB (System Information Block)
166ff827b4eSLinus Walleij 	 */
167ff827b4eSLinus Walleij 	if (fs.type == 2)
168ff827b4eSLinus Walleij 		return 0;
169ff827b4eSLinus Walleij 
170ff827b4eSLinus Walleij 	iis_ptr = fs.image_info_base & mask;
171ff827b4eSLinus Walleij 	img_ptr = fs.image_start & mask;
172ff827b4eSLinus Walleij 
173ff827b4eSLinus Walleij 	/*
174ff827b4eSLinus Walleij 	 * Check the image info base.  This can not
175ff827b4eSLinus Walleij 	 * be located after the footer structure.
176ff827b4eSLinus Walleij 	 */
177ff827b4eSLinus Walleij 	if (iis_ptr >= ptr)
178ff827b4eSLinus Walleij 		return 0;
179ff827b4eSLinus Walleij 
180ff827b4eSLinus Walleij 	/*
181ff827b4eSLinus Walleij 	 * Check the start of this image.  The image
182ff827b4eSLinus Walleij 	 * data can not be located after this block.
183ff827b4eSLinus Walleij 	 */
184ff827b4eSLinus Walleij 	if (img_ptr > off)
185ff827b4eSLinus Walleij 		return 0;
1864aeb1594SLinus Walleij 
1874aeb1594SLinus Walleij 	/* Read the image info block */
1884aeb1594SLinus Walleij 	ret = afs_read_iis_v1(mtd, &iis, iis_ptr);
1894aeb1594SLinus Walleij 	if (ret < 0)
1904aeb1594SLinus Walleij 		return ret;
1914aeb1594SLinus Walleij 
1924aeb1594SLinus Walleij 	part->name = kstrdup(iis.name, GFP_KERNEL);
1934aeb1594SLinus Walleij 	if (!part->name)
1944aeb1594SLinus Walleij 		return -ENOMEM;
1954aeb1594SLinus Walleij 
1964aeb1594SLinus Walleij 	part->size = (iis.length + mtd->erasesize - 1) & ~(mtd->erasesize - 1);
1974aeb1594SLinus Walleij 	part->offset = img_ptr;
1984aeb1594SLinus Walleij 	part->mask_flags = 0;
1994aeb1594SLinus Walleij 
2004aeb1594SLinus Walleij 	printk("  mtd: at 0x%08x, %5lluKiB, %8u, %s\n",
2014aeb1594SLinus Walleij 	       img_ptr, part->size / 1024,
2024aeb1594SLinus Walleij 	       iis.imageNumber, part->name);
2034aeb1594SLinus Walleij 
2044aeb1594SLinus Walleij 	return 0;
2054aeb1594SLinus Walleij }
2064aeb1594SLinus Walleij 
2074aeb1594SLinus Walleij static int parse_afs_partitions(struct mtd_info *mtd,
2084aeb1594SLinus Walleij 				const struct mtd_partition **pparts,
2094aeb1594SLinus Walleij 				struct mtd_part_parser_data *data)
2104aeb1594SLinus Walleij {
2114aeb1594SLinus Walleij 	struct mtd_partition *parts;
2124aeb1594SLinus Walleij 	u_int off, sz;
2134aeb1594SLinus Walleij 	int ret = 0;
2144aeb1594SLinus Walleij 	int i;
2154aeb1594SLinus Walleij 
21620700171SLinus Walleij 	/* Count the partitions by looping over all erase blocks */
2171fca1f6aSLinus Walleij 	for (i = off = sz = 0; off < mtd->size; off += mtd->erasesize) {
21820700171SLinus Walleij 		if (afs_is_v1(mtd, off)) {
2191fca1f6aSLinus Walleij 			sz += sizeof(struct mtd_partition);
2201fca1f6aSLinus Walleij 			i += 1;
2211fca1f6aSLinus Walleij 		}
2221fca1f6aSLinus Walleij 	}
2231fca1f6aSLinus Walleij 
2241fca1f6aSLinus Walleij 	if (!i)
2251fca1f6aSLinus Walleij 		return 0;
2262aa3b8e1SLinus Walleij 
2272aa3b8e1SLinus Walleij 	parts = kzalloc(sz, GFP_KERNEL);
2282aa3b8e1SLinus Walleij 	if (!parts)
2292aa3b8e1SLinus Walleij 		return -ENOMEM;
2302aa3b8e1SLinus Walleij 
2312aa3b8e1SLinus Walleij 	/*
2322aa3b8e1SLinus Walleij 	 * Identify the partitions
2332aa3b8e1SLinus Walleij 	 */
2341fca1f6aSLinus Walleij 	for (i = off = 0; off < mtd->size; off += mtd->erasesize) {
2352aa3b8e1SLinus Walleij 
2364aeb1594SLinus Walleij 		if (afs_is_v1(mtd, off)) {
2374aeb1594SLinus Walleij 			ret = afs_parse_v1_partition(mtd, off, &parts[i]);
2384aeb1594SLinus Walleij 			if (ret)
2391fca1f6aSLinus Walleij 				goto out_free_parts;
2404aeb1594SLinus Walleij 			i++;
2412aa3b8e1SLinus Walleij 		}
2422aa3b8e1SLinus Walleij 	}
2432aa3b8e1SLinus Walleij 
2442aa3b8e1SLinus Walleij 	*pparts = parts;
2451fca1f6aSLinus Walleij 	return i;
2461fca1f6aSLinus Walleij 
2471fca1f6aSLinus Walleij out_free_parts:
2481fca1f6aSLinus Walleij 	while (i >= 0) {
2491fca1f6aSLinus Walleij 		if (parts[i].name)
2501fca1f6aSLinus Walleij 			kfree(parts[i].name);
2511fca1f6aSLinus Walleij 		i--;
2521fca1f6aSLinus Walleij 	}
2531fca1f6aSLinus Walleij 	kfree(parts);
2541fca1f6aSLinus Walleij 	*pparts = NULL;
2551fca1f6aSLinus Walleij 	return ret;
2562aa3b8e1SLinus Walleij }
2572aa3b8e1SLinus Walleij 
25822749bf5SLinus Walleij static const struct of_device_id mtd_parser_afs_of_match_table[] = {
25922749bf5SLinus Walleij 	{ .compatible = "arm,arm-firmware-suite" },
26022749bf5SLinus Walleij 	{},
26122749bf5SLinus Walleij };
26222749bf5SLinus Walleij MODULE_DEVICE_TABLE(of, mtd_parser_afs_of_match_table);
26322749bf5SLinus Walleij 
2642aa3b8e1SLinus Walleij static struct mtd_part_parser afs_parser = {
2652aa3b8e1SLinus Walleij 	.parse_fn = parse_afs_partitions,
2662aa3b8e1SLinus Walleij 	.name = "afs",
26722749bf5SLinus Walleij 	.of_match_table = mtd_parser_afs_of_match_table,
2682aa3b8e1SLinus Walleij };
2692aa3b8e1SLinus Walleij module_mtd_part_parser(afs_parser);
2702aa3b8e1SLinus Walleij 
2712aa3b8e1SLinus Walleij MODULE_AUTHOR("ARM Ltd");
2722aa3b8e1SLinus Walleij MODULE_DESCRIPTION("ARM Firmware Suite partition parser");
2732aa3b8e1SLinus Walleij MODULE_LICENSE("GPL");
274