xref: /openbmc/linux/drivers/mtd/parsers/afs.c (revision 2aa3b8e1)
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;
1692aa3b8e1SLinus Walleij 	u_int mask, off, idx, sz;
1702aa3b8e1SLinus Walleij 	int ret = 0;
1712aa3b8e1SLinus Walleij 	char *str;
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 	 */
1842aa3b8e1SLinus Walleij 	for (idx = off = sz = 0; off < mtd->size; off += mtd->erasesize) {
1852aa3b8e1SLinus Walleij 		struct image_info_v1 iis;
1862aa3b8e1SLinus Walleij 		u_int iis_ptr, img_ptr;
1872aa3b8e1SLinus Walleij 
1882aa3b8e1SLinus Walleij 		ret = afs_read_footer_v1(mtd, &img_ptr, &iis_ptr, off, mask);
1892aa3b8e1SLinus Walleij 		if (ret < 0)
1902aa3b8e1SLinus Walleij 			break;
1912aa3b8e1SLinus Walleij 		if (ret) {
1922aa3b8e1SLinus Walleij 			ret = afs_read_iis_v1(mtd, &iis, iis_ptr);
1932aa3b8e1SLinus Walleij 			if (ret < 0)
1942aa3b8e1SLinus Walleij 				break;
1952aa3b8e1SLinus Walleij 			if (ret == 0)
1962aa3b8e1SLinus Walleij 				continue;
1972aa3b8e1SLinus Walleij 
1982aa3b8e1SLinus Walleij 			sz += sizeof(struct mtd_partition);
1992aa3b8e1SLinus Walleij 			sz += strlen(iis.name) + 1;
2002aa3b8e1SLinus Walleij 			idx += 1;
2012aa3b8e1SLinus Walleij 		}
2022aa3b8e1SLinus Walleij 	}
2032aa3b8e1SLinus Walleij 
2042aa3b8e1SLinus Walleij 	if (!sz)
2052aa3b8e1SLinus Walleij 		return ret;
2062aa3b8e1SLinus Walleij 
2072aa3b8e1SLinus Walleij 	parts = kzalloc(sz, GFP_KERNEL);
2082aa3b8e1SLinus Walleij 	if (!parts)
2092aa3b8e1SLinus Walleij 		return -ENOMEM;
2102aa3b8e1SLinus Walleij 
2112aa3b8e1SLinus Walleij 	str = (char *)(parts + idx);
2122aa3b8e1SLinus Walleij 
2132aa3b8e1SLinus Walleij 	/*
2142aa3b8e1SLinus Walleij 	 * Identify the partitions
2152aa3b8e1SLinus Walleij 	 */
2162aa3b8e1SLinus Walleij 	for (idx = off = 0; off < mtd->size; off += mtd->erasesize) {
2172aa3b8e1SLinus Walleij 		struct image_info_v1 iis;
2182aa3b8e1SLinus Walleij 		u_int iis_ptr, img_ptr;
2192aa3b8e1SLinus Walleij 
2202aa3b8e1SLinus Walleij 		/* Read the footer. */
2212aa3b8e1SLinus Walleij 		ret = afs_read_footer_v1(mtd, &img_ptr, &iis_ptr, off, mask);
2222aa3b8e1SLinus Walleij 		if (ret < 0)
2232aa3b8e1SLinus Walleij 			break;
2242aa3b8e1SLinus Walleij 		if (ret == 0)
2252aa3b8e1SLinus Walleij 			continue;
2262aa3b8e1SLinus Walleij 
2272aa3b8e1SLinus Walleij 		/* Read the image info block */
2282aa3b8e1SLinus Walleij 		ret = afs_read_iis_v1(mtd, &iis, iis_ptr);
2292aa3b8e1SLinus Walleij 		if (ret < 0)
2302aa3b8e1SLinus Walleij 			break;
2312aa3b8e1SLinus Walleij 		if (ret == 0)
2322aa3b8e1SLinus Walleij 			continue;
2332aa3b8e1SLinus Walleij 
2342aa3b8e1SLinus Walleij 		strcpy(str, iis.name);
2352aa3b8e1SLinus Walleij 
2362aa3b8e1SLinus Walleij 		parts[idx].name		= str;
2372aa3b8e1SLinus Walleij 		parts[idx].size		= (iis.length + mtd->erasesize - 1) & ~(mtd->erasesize - 1);
2382aa3b8e1SLinus Walleij 		parts[idx].offset	= img_ptr;
2392aa3b8e1SLinus Walleij 		parts[idx].mask_flags	= 0;
2402aa3b8e1SLinus Walleij 
2412aa3b8e1SLinus Walleij 		printk("  mtd%d: at 0x%08x, %5lluKiB, %8u, %s\n",
2422aa3b8e1SLinus Walleij 			idx, img_ptr, parts[idx].size / 1024,
2432aa3b8e1SLinus Walleij 			iis.imageNumber, str);
2442aa3b8e1SLinus Walleij 
2452aa3b8e1SLinus Walleij 		idx += 1;
2462aa3b8e1SLinus Walleij 		str = str + strlen(iis.name) + 1;
2472aa3b8e1SLinus Walleij 	}
2482aa3b8e1SLinus Walleij 
2492aa3b8e1SLinus Walleij 	if (!idx) {
2502aa3b8e1SLinus Walleij 		kfree(parts);
2512aa3b8e1SLinus Walleij 		parts = NULL;
2522aa3b8e1SLinus Walleij 	}
2532aa3b8e1SLinus Walleij 
2542aa3b8e1SLinus Walleij 	*pparts = parts;
2552aa3b8e1SLinus Walleij 	return idx ? idx : ret;
2562aa3b8e1SLinus Walleij }
2572aa3b8e1SLinus Walleij 
2582aa3b8e1SLinus Walleij static struct mtd_part_parser afs_parser = {
2592aa3b8e1SLinus Walleij 	.parse_fn = parse_afs_partitions,
2602aa3b8e1SLinus Walleij 	.name = "afs",
2612aa3b8e1SLinus Walleij };
2622aa3b8e1SLinus Walleij module_mtd_part_parser(afs_parser);
2632aa3b8e1SLinus Walleij 
2642aa3b8e1SLinus Walleij MODULE_AUTHOR("ARM Ltd");
2652aa3b8e1SLinus Walleij MODULE_DESCRIPTION("ARM Firmware Suite partition parser");
2662aa3b8e1SLinus Walleij MODULE_LICENSE("GPL");
267