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