xref: /openbmc/linux/drivers/mtd/parsers/afs.c (revision 1fca1f6a)
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 
712aa3b8e1SLinus Walleij static int
722aa3b8e1SLinus Walleij afs_read_footer_v1(struct mtd_info *mtd, u_int *img_start, u_int *iis_start,
732aa3b8e1SLinus Walleij 		   u_int off, u_int mask)
742aa3b8e1SLinus Walleij {
752aa3b8e1SLinus Walleij 	struct footer_v1 fs;
762aa3b8e1SLinus Walleij 	u_int ptr = off + mtd->erasesize - sizeof(fs);
772aa3b8e1SLinus Walleij 	size_t sz;
782aa3b8e1SLinus Walleij 	int ret;
792aa3b8e1SLinus Walleij 
802aa3b8e1SLinus Walleij 	ret = mtd_read(mtd, ptr, sizeof(fs), &sz, (u_char *)&fs);
812aa3b8e1SLinus Walleij 	if (ret >= 0 && sz != sizeof(fs))
822aa3b8e1SLinus Walleij 		ret = -EINVAL;
832aa3b8e1SLinus Walleij 
842aa3b8e1SLinus Walleij 	if (ret < 0) {
852aa3b8e1SLinus Walleij 		printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
862aa3b8e1SLinus Walleij 			ptr, ret);
872aa3b8e1SLinus Walleij 		return ret;
882aa3b8e1SLinus Walleij 	}
892aa3b8e1SLinus Walleij 
902aa3b8e1SLinus Walleij 	/*
912aa3b8e1SLinus Walleij 	 * Does it contain the magic number?
922aa3b8e1SLinus Walleij 	 */
932aa3b8e1SLinus Walleij 	if (fs.signature != AFSV1_FOOTER_MAGIC)
942aa3b8e1SLinus Walleij 		return 0;
952aa3b8e1SLinus Walleij 
962aa3b8e1SLinus Walleij 	/*
972aa3b8e1SLinus Walleij 	 * Check the checksum.
982aa3b8e1SLinus Walleij 	 */
992aa3b8e1SLinus Walleij 	if (word_sum(&fs, sizeof(fs) / sizeof(u32)) != 0xffffffff)
1002aa3b8e1SLinus Walleij 		return 0;
1012aa3b8e1SLinus Walleij 
1022aa3b8e1SLinus Walleij 	/*
1032aa3b8e1SLinus Walleij 	 * Don't touch the SIB.
1042aa3b8e1SLinus Walleij 	 */
1052aa3b8e1SLinus Walleij 	if (fs.type == 2)
1062aa3b8e1SLinus Walleij 		return 0;
1072aa3b8e1SLinus Walleij 
1082aa3b8e1SLinus Walleij 	*iis_start = fs.image_info_base & mask;
1092aa3b8e1SLinus Walleij 	*img_start = fs.image_start & mask;
1102aa3b8e1SLinus Walleij 
1112aa3b8e1SLinus Walleij 	/*
1122aa3b8e1SLinus Walleij 	 * Check the image info base.  This can not
1132aa3b8e1SLinus Walleij 	 * be located after the footer structure.
1142aa3b8e1SLinus Walleij 	 */
1152aa3b8e1SLinus Walleij 	if (*iis_start >= ptr)
1162aa3b8e1SLinus Walleij 		return 0;
1172aa3b8e1SLinus Walleij 
1182aa3b8e1SLinus Walleij 	/*
1192aa3b8e1SLinus Walleij 	 * Check the start of this image.  The image
1202aa3b8e1SLinus Walleij 	 * data can not be located after this block.
1212aa3b8e1SLinus Walleij 	 */
1222aa3b8e1SLinus Walleij 	if (*img_start > off)
1232aa3b8e1SLinus Walleij 		return 0;
1242aa3b8e1SLinus Walleij 
1252aa3b8e1SLinus Walleij 	return 1;
1262aa3b8e1SLinus Walleij }
1272aa3b8e1SLinus Walleij 
1282aa3b8e1SLinus Walleij static int
1292aa3b8e1SLinus Walleij afs_read_iis_v1(struct mtd_info *mtd, struct image_info_v1 *iis, u_int ptr)
1302aa3b8e1SLinus Walleij {
1312aa3b8e1SLinus Walleij 	size_t sz;
1322aa3b8e1SLinus Walleij 	int ret, i;
1332aa3b8e1SLinus Walleij 
1342aa3b8e1SLinus Walleij 	memset(iis, 0, sizeof(*iis));
1352aa3b8e1SLinus Walleij 	ret = mtd_read(mtd, ptr, sizeof(*iis), &sz, (u_char *)iis);
1362aa3b8e1SLinus Walleij 	if (ret < 0)
1372aa3b8e1SLinus Walleij 		goto failed;
1382aa3b8e1SLinus Walleij 
1392aa3b8e1SLinus Walleij 	if (sz != sizeof(*iis)) {
1402aa3b8e1SLinus Walleij 		ret = -EINVAL;
1412aa3b8e1SLinus Walleij 		goto failed;
1422aa3b8e1SLinus Walleij 	}
1432aa3b8e1SLinus Walleij 
1442aa3b8e1SLinus Walleij 	ret = 0;
1452aa3b8e1SLinus Walleij 
1462aa3b8e1SLinus Walleij 	/*
1472aa3b8e1SLinus Walleij 	 * Validate the name - it must be NUL terminated.
1482aa3b8e1SLinus Walleij 	 */
1492aa3b8e1SLinus Walleij 	for (i = 0; i < sizeof(iis->name); i++)
1502aa3b8e1SLinus Walleij 		if (iis->name[i] == '\0')
1512aa3b8e1SLinus Walleij 			break;
1522aa3b8e1SLinus Walleij 
1532aa3b8e1SLinus Walleij 	if (i < sizeof(iis->name))
1542aa3b8e1SLinus Walleij 		ret = 1;
1552aa3b8e1SLinus Walleij 
1562aa3b8e1SLinus Walleij 	return ret;
1572aa3b8e1SLinus Walleij 
1582aa3b8e1SLinus Walleij  failed:
1592aa3b8e1SLinus Walleij 	printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
1602aa3b8e1SLinus Walleij 		ptr, ret);
1612aa3b8e1SLinus Walleij 	return ret;
1622aa3b8e1SLinus Walleij }
1632aa3b8e1SLinus Walleij 
1642aa3b8e1SLinus Walleij static int parse_afs_partitions(struct mtd_info *mtd,
1652aa3b8e1SLinus Walleij 				const struct mtd_partition **pparts,
1662aa3b8e1SLinus Walleij 				struct mtd_part_parser_data *data)
1672aa3b8e1SLinus Walleij {
1682aa3b8e1SLinus Walleij 	struct mtd_partition *parts;
1691fca1f6aSLinus Walleij 	u_int mask, off, sz;
1702aa3b8e1SLinus Walleij 	int ret = 0;
1711fca1f6aSLinus Walleij 	int i;
1722aa3b8e1SLinus Walleij 
1732aa3b8e1SLinus Walleij 	/*
1742aa3b8e1SLinus Walleij 	 * This is the address mask; we use this to mask off out of
1752aa3b8e1SLinus Walleij 	 * range address bits.
1762aa3b8e1SLinus Walleij 	 */
1772aa3b8e1SLinus Walleij 	mask = mtd->size - 1;
1782aa3b8e1SLinus Walleij 
1792aa3b8e1SLinus Walleij 	/*
1802aa3b8e1SLinus Walleij 	 * First, calculate the size of the array we need for the
1812aa3b8e1SLinus Walleij 	 * partition information.  We include in this the size of
1822aa3b8e1SLinus Walleij 	 * the strings.
1832aa3b8e1SLinus Walleij 	 */
1841fca1f6aSLinus Walleij 	for (i = off = sz = 0; off < mtd->size; off += mtd->erasesize) {
1852aa3b8e1SLinus Walleij 		u_int iis_ptr, img_ptr;
1862aa3b8e1SLinus Walleij 
1872aa3b8e1SLinus Walleij 		ret = afs_read_footer_v1(mtd, &img_ptr, &iis_ptr, off, mask);
1882aa3b8e1SLinus Walleij 		if (ret < 0)
1892aa3b8e1SLinus Walleij 			return ret;
1901fca1f6aSLinus Walleij 		if (ret) {
1911fca1f6aSLinus Walleij 			sz += sizeof(struct mtd_partition);
1921fca1f6aSLinus Walleij 			i += 1;
1931fca1f6aSLinus Walleij 		}
1941fca1f6aSLinus Walleij 	}
1951fca1f6aSLinus Walleij 
1961fca1f6aSLinus Walleij 	if (!i)
1971fca1f6aSLinus Walleij 		return 0;
1982aa3b8e1SLinus Walleij 
1992aa3b8e1SLinus Walleij 	parts = kzalloc(sz, GFP_KERNEL);
2002aa3b8e1SLinus Walleij 	if (!parts)
2012aa3b8e1SLinus Walleij 		return -ENOMEM;
2022aa3b8e1SLinus Walleij 
2032aa3b8e1SLinus Walleij 	/*
2042aa3b8e1SLinus Walleij 	 * Identify the partitions
2052aa3b8e1SLinus Walleij 	 */
2061fca1f6aSLinus Walleij 	for (i = off = 0; off < mtd->size; off += mtd->erasesize) {
2072aa3b8e1SLinus Walleij 		struct image_info_v1 iis;
2082aa3b8e1SLinus Walleij 		u_int iis_ptr, img_ptr;
2092aa3b8e1SLinus Walleij 
2102aa3b8e1SLinus Walleij 		/* Read the footer. */
2112aa3b8e1SLinus Walleij 		ret = afs_read_footer_v1(mtd, &img_ptr, &iis_ptr, off, mask);
2122aa3b8e1SLinus Walleij 		if (ret < 0)
2131fca1f6aSLinus Walleij 			goto out_free_parts;
2142aa3b8e1SLinus Walleij 		if (ret == 0)
2152aa3b8e1SLinus Walleij 			continue;
2162aa3b8e1SLinus Walleij 
2172aa3b8e1SLinus Walleij 		/* Read the image info block */
2182aa3b8e1SLinus Walleij 		ret = afs_read_iis_v1(mtd, &iis, iis_ptr);
2192aa3b8e1SLinus Walleij 		if (ret < 0)
2201fca1f6aSLinus Walleij 			goto out_free_parts;
2212aa3b8e1SLinus Walleij 		if (ret == 0)
2222aa3b8e1SLinus Walleij 			continue;
2232aa3b8e1SLinus Walleij 
2241fca1f6aSLinus Walleij 		parts[i].name = kstrdup(iis.name, GFP_KERNEL);
2251fca1f6aSLinus Walleij 		if (!parts[i].name) {
2261fca1f6aSLinus Walleij 			ret = -ENOMEM;
2271fca1f6aSLinus Walleij 			goto out_free_parts;
2282aa3b8e1SLinus Walleij 		}
2292aa3b8e1SLinus Walleij 
2301fca1f6aSLinus Walleij 		parts[i].size		= (iis.length + mtd->erasesize - 1) & ~(mtd->erasesize - 1);
2311fca1f6aSLinus Walleij 		parts[i].offset	= img_ptr;
2321fca1f6aSLinus Walleij 		parts[i].mask_flags	= 0;
2331fca1f6aSLinus Walleij 
2341fca1f6aSLinus Walleij 		printk("  mtd%d: at 0x%08x, %5lluKiB, %8u, %s\n",
2351fca1f6aSLinus Walleij 			i, img_ptr, parts[i].size / 1024,
2361fca1f6aSLinus Walleij 			iis.imageNumber, parts[i].name);
2371fca1f6aSLinus Walleij 
2381fca1f6aSLinus Walleij 		i += 1;
2392aa3b8e1SLinus Walleij 	}
2402aa3b8e1SLinus Walleij 
2412aa3b8e1SLinus Walleij 	*pparts = parts;
2421fca1f6aSLinus Walleij 	return i;
2431fca1f6aSLinus Walleij 
2441fca1f6aSLinus Walleij out_free_parts:
2451fca1f6aSLinus Walleij 	while (i >= 0) {
2461fca1f6aSLinus Walleij 		if (parts[i].name)
2471fca1f6aSLinus Walleij 			kfree(parts[i].name);
2481fca1f6aSLinus Walleij 		i--;
2491fca1f6aSLinus Walleij 	}
2501fca1f6aSLinus Walleij 	kfree(parts);
2511fca1f6aSLinus Walleij 	*pparts = NULL;
2521fca1f6aSLinus Walleij 	return ret;
2532aa3b8e1SLinus Walleij }
2542aa3b8e1SLinus Walleij 
25522749bf5SLinus Walleij static const struct of_device_id mtd_parser_afs_of_match_table[] = {
25622749bf5SLinus Walleij 	{ .compatible = "arm,arm-firmware-suite" },
25722749bf5SLinus Walleij 	{},
25822749bf5SLinus Walleij };
25922749bf5SLinus Walleij MODULE_DEVICE_TABLE(of, mtd_parser_afs_of_match_table);
26022749bf5SLinus Walleij 
2612aa3b8e1SLinus Walleij static struct mtd_part_parser afs_parser = {
2622aa3b8e1SLinus Walleij 	.parse_fn = parse_afs_partitions,
2632aa3b8e1SLinus Walleij 	.name = "afs",
26422749bf5SLinus Walleij 	.of_match_table = mtd_parser_afs_of_match_table,
2652aa3b8e1SLinus Walleij };
2662aa3b8e1SLinus Walleij module_mtd_part_parser(afs_parser);
2672aa3b8e1SLinus Walleij 
2682aa3b8e1SLinus Walleij MODULE_AUTHOR("ARM Ltd");
2692aa3b8e1SLinus Walleij MODULE_DESCRIPTION("ARM Firmware Suite partition parser");
2702aa3b8e1SLinus Walleij MODULE_LICENSE("GPL");
271