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