18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/*====================================================================== 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci drivers/mtd/afs.c: ARM Flash Layout/Partitioning 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci Copyright © 2000 ARM Limited 78c2ecf20Sopenharmony_ci Copyright (C) 2019 Linus Walleij 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci This is access code for flashes using ARM's flash partitioning 118c2ecf20Sopenharmony_ci standards. 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci======================================================================*/ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/types.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/string.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 238c2ecf20Sopenharmony_ci#include <linux/mtd/map.h> 248c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define AFSV1_FOOTER_MAGIC 0xA0FFFF9F 278c2ecf20Sopenharmony_ci#define AFSV2_FOOTER_MAGIC1 0x464C5348 /* "FLSH" */ 288c2ecf20Sopenharmony_ci#define AFSV2_FOOTER_MAGIC2 0x464F4F54 /* "FOOT" */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct footer_v1 { 318c2ecf20Sopenharmony_ci u32 image_info_base; /* Address of first word of ImageFooter */ 328c2ecf20Sopenharmony_ci u32 image_start; /* Start of area reserved by this footer */ 338c2ecf20Sopenharmony_ci u32 signature; /* 'Magic' number proves it's a footer */ 348c2ecf20Sopenharmony_ci u32 type; /* Area type: ARM Image, SIB, customer */ 358c2ecf20Sopenharmony_ci u32 checksum; /* Just this structure */ 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct image_info_v1 { 398c2ecf20Sopenharmony_ci u32 bootFlags; /* Boot flags, compression etc. */ 408c2ecf20Sopenharmony_ci u32 imageNumber; /* Unique number, selects for boot etc. */ 418c2ecf20Sopenharmony_ci u32 loadAddress; /* Address program should be loaded to */ 428c2ecf20Sopenharmony_ci u32 length; /* Actual size of image */ 438c2ecf20Sopenharmony_ci u32 address; /* Image is executed from here */ 448c2ecf20Sopenharmony_ci char name[16]; /* Null terminated */ 458c2ecf20Sopenharmony_ci u32 headerBase; /* Flash Address of any stripped header */ 468c2ecf20Sopenharmony_ci u32 header_length; /* Length of header in memory */ 478c2ecf20Sopenharmony_ci u32 headerType; /* AIF, RLF, s-record etc. */ 488c2ecf20Sopenharmony_ci u32 checksum; /* Image checksum (inc. this struct) */ 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic u32 word_sum(void *words, int num) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci u32 *p = words; 548c2ecf20Sopenharmony_ci u32 sum = 0; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci while (num--) 578c2ecf20Sopenharmony_ci sum += *p++; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return sum; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic u32 word_sum_v2(u32 *p, u32 num) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci u32 sum = 0; 658c2ecf20Sopenharmony_ci int i; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 688c2ecf20Sopenharmony_ci u32 val; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci val = p[i]; 718c2ecf20Sopenharmony_ci if (val > ~sum) 728c2ecf20Sopenharmony_ci sum++; 738c2ecf20Sopenharmony_ci sum += val; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci return ~sum; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic bool afs_is_v1(struct mtd_info *mtd, u_int off) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci /* The magic is 12 bytes from the end of the erase block */ 818c2ecf20Sopenharmony_ci u_int ptr = off + mtd->erasesize - 12; 828c2ecf20Sopenharmony_ci u32 magic; 838c2ecf20Sopenharmony_ci size_t sz; 848c2ecf20Sopenharmony_ci int ret; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci ret = mtd_read(mtd, ptr, 4, &sz, (u_char *)&magic); 878c2ecf20Sopenharmony_ci if (ret < 0) { 888c2ecf20Sopenharmony_ci printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n", 898c2ecf20Sopenharmony_ci ptr, ret); 908c2ecf20Sopenharmony_ci return false; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci if (ret >= 0 && sz != 4) 938c2ecf20Sopenharmony_ci return false; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return (magic == AFSV1_FOOTER_MAGIC); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic bool afs_is_v2(struct mtd_info *mtd, u_int off) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci /* The magic is the 8 last bytes of the erase block */ 1018c2ecf20Sopenharmony_ci u_int ptr = off + mtd->erasesize - 8; 1028c2ecf20Sopenharmony_ci u32 foot[2]; 1038c2ecf20Sopenharmony_ci size_t sz; 1048c2ecf20Sopenharmony_ci int ret; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci ret = mtd_read(mtd, ptr, 8, &sz, (u_char *)foot); 1078c2ecf20Sopenharmony_ci if (ret < 0) { 1088c2ecf20Sopenharmony_ci printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n", 1098c2ecf20Sopenharmony_ci ptr, ret); 1108c2ecf20Sopenharmony_ci return false; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci if (ret >= 0 && sz != 8) 1138c2ecf20Sopenharmony_ci return false; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return (foot[0] == AFSV2_FOOTER_MAGIC1 && 1168c2ecf20Sopenharmony_ci foot[1] == AFSV2_FOOTER_MAGIC2); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int afs_parse_v1_partition(struct mtd_info *mtd, 1208c2ecf20Sopenharmony_ci u_int off, struct mtd_partition *part) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct footer_v1 fs; 1238c2ecf20Sopenharmony_ci struct image_info_v1 iis; 1248c2ecf20Sopenharmony_ci u_int mask; 1258c2ecf20Sopenharmony_ci /* 1268c2ecf20Sopenharmony_ci * Static checks cannot see that we bail out if we have an error 1278c2ecf20Sopenharmony_ci * reading the footer. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci u_int iis_ptr; 1308c2ecf20Sopenharmony_ci u_int img_ptr; 1318c2ecf20Sopenharmony_ci u_int ptr; 1328c2ecf20Sopenharmony_ci size_t sz; 1338c2ecf20Sopenharmony_ci int ret; 1348c2ecf20Sopenharmony_ci int i; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* 1378c2ecf20Sopenharmony_ci * This is the address mask; we use this to mask off out of 1388c2ecf20Sopenharmony_ci * range address bits. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci mask = mtd->size - 1; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci ptr = off + mtd->erasesize - sizeof(fs); 1438c2ecf20Sopenharmony_ci ret = mtd_read(mtd, ptr, sizeof(fs), &sz, (u_char *)&fs); 1448c2ecf20Sopenharmony_ci if (ret >= 0 && sz != sizeof(fs)) 1458c2ecf20Sopenharmony_ci ret = -EINVAL; 1468c2ecf20Sopenharmony_ci if (ret < 0) { 1478c2ecf20Sopenharmony_ci printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n", 1488c2ecf20Sopenharmony_ci ptr, ret); 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci /* 1528c2ecf20Sopenharmony_ci * Check the checksum. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci if (word_sum(&fs, sizeof(fs) / sizeof(u32)) != 0xffffffff) 1558c2ecf20Sopenharmony_ci return -EINVAL; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* 1588c2ecf20Sopenharmony_ci * Hide the SIB (System Information Block) 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci if (fs.type == 2) 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci iis_ptr = fs.image_info_base & mask; 1648c2ecf20Sopenharmony_ci img_ptr = fs.image_start & mask; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* 1678c2ecf20Sopenharmony_ci * Check the image info base. This can not 1688c2ecf20Sopenharmony_ci * be located after the footer structure. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci if (iis_ptr >= ptr) 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * Check the start of this image. The image 1758c2ecf20Sopenharmony_ci * data can not be located after this block. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci if (img_ptr > off) 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* Read the image info block */ 1818c2ecf20Sopenharmony_ci memset(&iis, 0, sizeof(iis)); 1828c2ecf20Sopenharmony_ci ret = mtd_read(mtd, iis_ptr, sizeof(iis), &sz, (u_char *)&iis); 1838c2ecf20Sopenharmony_ci if (ret < 0) { 1848c2ecf20Sopenharmony_ci printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n", 1858c2ecf20Sopenharmony_ci iis_ptr, ret); 1868c2ecf20Sopenharmony_ci return -EINVAL; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (sz != sizeof(iis)) 1908c2ecf20Sopenharmony_ci return -EINVAL; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* 1938c2ecf20Sopenharmony_ci * Validate the name - it must be NUL terminated. 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(iis.name); i++) 1968c2ecf20Sopenharmony_ci if (iis.name[i] == '\0') 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci if (i > sizeof(iis.name)) 1998c2ecf20Sopenharmony_ci return -EINVAL; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci part->name = kstrdup(iis.name, GFP_KERNEL); 2028c2ecf20Sopenharmony_ci if (!part->name) 2038c2ecf20Sopenharmony_ci return -ENOMEM; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci part->size = (iis.length + mtd->erasesize - 1) & ~(mtd->erasesize - 1); 2068c2ecf20Sopenharmony_ci part->offset = img_ptr; 2078c2ecf20Sopenharmony_ci part->mask_flags = 0; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci printk(" mtd: at 0x%08x, %5lluKiB, %8u, %s\n", 2108c2ecf20Sopenharmony_ci img_ptr, part->size / 1024, 2118c2ecf20Sopenharmony_ci iis.imageNumber, part->name); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic int afs_parse_v2_partition(struct mtd_info *mtd, 2178c2ecf20Sopenharmony_ci u_int off, struct mtd_partition *part) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci u_int ptr; 2208c2ecf20Sopenharmony_ci u32 footer[12]; 2218c2ecf20Sopenharmony_ci u32 imginfo[36]; 2228c2ecf20Sopenharmony_ci char *name; 2238c2ecf20Sopenharmony_ci u32 version; 2248c2ecf20Sopenharmony_ci u32 entrypoint; 2258c2ecf20Sopenharmony_ci u32 attributes; 2268c2ecf20Sopenharmony_ci u32 region_count; 2278c2ecf20Sopenharmony_ci u32 block_start; 2288c2ecf20Sopenharmony_ci u32 block_end; 2298c2ecf20Sopenharmony_ci u32 crc; 2308c2ecf20Sopenharmony_ci size_t sz; 2318c2ecf20Sopenharmony_ci int ret; 2328c2ecf20Sopenharmony_ci int i; 2338c2ecf20Sopenharmony_ci int pad = 0; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci pr_debug("Parsing v2 partition @%08x-%08x\n", 2368c2ecf20Sopenharmony_ci off, off + mtd->erasesize); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* First read the footer */ 2398c2ecf20Sopenharmony_ci ptr = off + mtd->erasesize - sizeof(footer); 2408c2ecf20Sopenharmony_ci ret = mtd_read(mtd, ptr, sizeof(footer), &sz, (u_char *)footer); 2418c2ecf20Sopenharmony_ci if ((ret < 0) || (ret >= 0 && sz != sizeof(footer))) { 2428c2ecf20Sopenharmony_ci pr_err("AFS: mtd read failed at 0x%x: %d\n", 2438c2ecf20Sopenharmony_ci ptr, ret); 2448c2ecf20Sopenharmony_ci return -EIO; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci name = (char *) &footer[0]; 2478c2ecf20Sopenharmony_ci version = footer[9]; 2488c2ecf20Sopenharmony_ci ptr = off + mtd->erasesize - sizeof(footer) - footer[8]; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci pr_debug("found image \"%s\", version %08x, info @%08x\n", 2518c2ecf20Sopenharmony_ci name, version, ptr); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* Then read the image information */ 2548c2ecf20Sopenharmony_ci ret = mtd_read(mtd, ptr, sizeof(imginfo), &sz, (u_char *)imginfo); 2558c2ecf20Sopenharmony_ci if ((ret < 0) || (ret >= 0 && sz != sizeof(imginfo))) { 2568c2ecf20Sopenharmony_ci pr_err("AFS: mtd read failed at 0x%x: %d\n", 2578c2ecf20Sopenharmony_ci ptr, ret); 2588c2ecf20Sopenharmony_ci return -EIO; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* 32bit platforms have 4 bytes padding */ 2628c2ecf20Sopenharmony_ci crc = word_sum_v2(&imginfo[1], 34); 2638c2ecf20Sopenharmony_ci if (!crc) { 2648c2ecf20Sopenharmony_ci pr_debug("Padding 1 word (4 bytes)\n"); 2658c2ecf20Sopenharmony_ci pad = 1; 2668c2ecf20Sopenharmony_ci } else { 2678c2ecf20Sopenharmony_ci /* 64bit platforms have 8 bytes padding */ 2688c2ecf20Sopenharmony_ci crc = word_sum_v2(&imginfo[2], 34); 2698c2ecf20Sopenharmony_ci if (!crc) { 2708c2ecf20Sopenharmony_ci pr_debug("Padding 2 words (8 bytes)\n"); 2718c2ecf20Sopenharmony_ci pad = 2; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci if (crc) { 2758c2ecf20Sopenharmony_ci pr_err("AFS: bad checksum on v2 image info: %08x\n", crc); 2768c2ecf20Sopenharmony_ci return -EINVAL; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci entrypoint = imginfo[pad]; 2798c2ecf20Sopenharmony_ci attributes = imginfo[pad+1]; 2808c2ecf20Sopenharmony_ci region_count = imginfo[pad+2]; 2818c2ecf20Sopenharmony_ci block_start = imginfo[20]; 2828c2ecf20Sopenharmony_ci block_end = imginfo[21]; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci pr_debug("image entry=%08x, attr=%08x, regions=%08x, " 2858c2ecf20Sopenharmony_ci "bs=%08x, be=%08x\n", 2868c2ecf20Sopenharmony_ci entrypoint, attributes, region_count, 2878c2ecf20Sopenharmony_ci block_start, block_end); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci for (i = 0; i < region_count; i++) { 2908c2ecf20Sopenharmony_ci u32 region_load_addr = imginfo[pad + 3 + i*4]; 2918c2ecf20Sopenharmony_ci u32 region_size = imginfo[pad + 4 + i*4]; 2928c2ecf20Sopenharmony_ci u32 region_offset = imginfo[pad + 5 + i*4]; 2938c2ecf20Sopenharmony_ci u32 region_start; 2948c2ecf20Sopenharmony_ci u32 region_end; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci pr_debug(" region %d: address: %08x, size: %08x, " 2978c2ecf20Sopenharmony_ci "offset: %08x\n", 2988c2ecf20Sopenharmony_ci i, 2998c2ecf20Sopenharmony_ci region_load_addr, 3008c2ecf20Sopenharmony_ci region_size, 3018c2ecf20Sopenharmony_ci region_offset); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci region_start = off + region_offset; 3048c2ecf20Sopenharmony_ci region_end = region_start + region_size; 3058c2ecf20Sopenharmony_ci /* Align partition to end of erase block */ 3068c2ecf20Sopenharmony_ci region_end += (mtd->erasesize - 1); 3078c2ecf20Sopenharmony_ci region_end &= ~(mtd->erasesize -1); 3088c2ecf20Sopenharmony_ci pr_debug(" partition start = %08x, partition end = %08x\n", 3098c2ecf20Sopenharmony_ci region_start, region_end); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* Create one partition per region */ 3128c2ecf20Sopenharmony_ci part->name = kstrdup(name, GFP_KERNEL); 3138c2ecf20Sopenharmony_ci if (!part->name) 3148c2ecf20Sopenharmony_ci return -ENOMEM; 3158c2ecf20Sopenharmony_ci part->offset = region_start; 3168c2ecf20Sopenharmony_ci part->size = region_end - region_start; 3178c2ecf20Sopenharmony_ci part->mask_flags = 0; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic int parse_afs_partitions(struct mtd_info *mtd, 3248c2ecf20Sopenharmony_ci const struct mtd_partition **pparts, 3258c2ecf20Sopenharmony_ci struct mtd_part_parser_data *data) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct mtd_partition *parts; 3288c2ecf20Sopenharmony_ci u_int off, sz; 3298c2ecf20Sopenharmony_ci int ret = 0; 3308c2ecf20Sopenharmony_ci int i; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* Count the partitions by looping over all erase blocks */ 3338c2ecf20Sopenharmony_ci for (i = off = sz = 0; off < mtd->size; off += mtd->erasesize) { 3348c2ecf20Sopenharmony_ci if (afs_is_v1(mtd, off)) { 3358c2ecf20Sopenharmony_ci sz += sizeof(struct mtd_partition); 3368c2ecf20Sopenharmony_ci i += 1; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci if (afs_is_v2(mtd, off)) { 3398c2ecf20Sopenharmony_ci sz += sizeof(struct mtd_partition); 3408c2ecf20Sopenharmony_ci i += 1; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (!i) 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci parts = kzalloc(sz, GFP_KERNEL); 3488c2ecf20Sopenharmony_ci if (!parts) 3498c2ecf20Sopenharmony_ci return -ENOMEM; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* 3528c2ecf20Sopenharmony_ci * Identify the partitions 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_ci for (i = off = 0; off < mtd->size; off += mtd->erasesize) { 3558c2ecf20Sopenharmony_ci if (afs_is_v1(mtd, off)) { 3568c2ecf20Sopenharmony_ci ret = afs_parse_v1_partition(mtd, off, &parts[i]); 3578c2ecf20Sopenharmony_ci if (ret) 3588c2ecf20Sopenharmony_ci goto out_free_parts; 3598c2ecf20Sopenharmony_ci i++; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci if (afs_is_v2(mtd, off)) { 3628c2ecf20Sopenharmony_ci ret = afs_parse_v2_partition(mtd, off, &parts[i]); 3638c2ecf20Sopenharmony_ci if (ret) 3648c2ecf20Sopenharmony_ci goto out_free_parts; 3658c2ecf20Sopenharmony_ci i++; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci *pparts = parts; 3708c2ecf20Sopenharmony_ci return i; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ciout_free_parts: 3738c2ecf20Sopenharmony_ci while (--i >= 0) 3748c2ecf20Sopenharmony_ci kfree(parts[i].name); 3758c2ecf20Sopenharmony_ci kfree(parts); 3768c2ecf20Sopenharmony_ci *pparts = NULL; 3778c2ecf20Sopenharmony_ci return ret; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic const struct of_device_id mtd_parser_afs_of_match_table[] = { 3818c2ecf20Sopenharmony_ci { .compatible = "arm,arm-firmware-suite" }, 3828c2ecf20Sopenharmony_ci {}, 3838c2ecf20Sopenharmony_ci}; 3848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtd_parser_afs_of_match_table); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic struct mtd_part_parser afs_parser = { 3878c2ecf20Sopenharmony_ci .parse_fn = parse_afs_partitions, 3888c2ecf20Sopenharmony_ci .name = "afs", 3898c2ecf20Sopenharmony_ci .of_match_table = mtd_parser_afs_of_match_table, 3908c2ecf20Sopenharmony_ci}; 3918c2ecf20Sopenharmony_cimodule_mtd_part_parser(afs_parser); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ciMODULE_AUTHOR("ARM Ltd"); 3948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ARM Firmware Suite partition parser"); 3958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 396