Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*======================================================================
0003 
0004     drivers/mtd/afs.c: ARM Flash Layout/Partitioning
0005 
0006     Copyright © 2000 ARM Limited
0007     Copyright (C) 2019 Linus Walleij
0008 
0009 
0010    This is access code for flashes using ARM's flash partitioning
0011    standards.
0012 
0013 ======================================================================*/
0014 
0015 #include <linux/module.h>
0016 #include <linux/types.h>
0017 #include <linux/kernel.h>
0018 #include <linux/slab.h>
0019 #include <linux/string.h>
0020 #include <linux/init.h>
0021 
0022 #include <linux/mtd/mtd.h>
0023 #include <linux/mtd/map.h>
0024 #include <linux/mtd/partitions.h>
0025 
0026 #define AFSV1_FOOTER_MAGIC 0xA0FFFF9F
0027 #define AFSV2_FOOTER_MAGIC1 0x464C5348 /* "FLSH" */
0028 #define AFSV2_FOOTER_MAGIC2 0x464F4F54 /* "FOOT" */
0029 
0030 struct footer_v1 {
0031     u32 image_info_base;    /* Address of first word of ImageFooter  */
0032     u32 image_start;    /* Start of area reserved by this footer */
0033     u32 signature;      /* 'Magic' number proves it's a footer   */
0034     u32 type;       /* Area type: ARM Image, SIB, customer   */
0035     u32 checksum;       /* Just this structure                   */
0036 };
0037 
0038 struct image_info_v1 {
0039     u32 bootFlags;      /* Boot flags, compression etc.          */
0040     u32 imageNumber;    /* Unique number, selects for boot etc.  */
0041     u32 loadAddress;    /* Address program should be loaded to   */
0042     u32 length;     /* Actual size of image                  */
0043     u32 address;        /* Image is executed from here           */
0044     char name[16];      /* Null terminated                       */
0045     u32 headerBase;     /* Flash Address of any stripped header  */
0046     u32 header_length;  /* Length of header in memory            */
0047     u32 headerType;     /* AIF, RLF, s-record etc.               */
0048     u32 checksum;       /* Image checksum (inc. this struct)     */
0049 };
0050 
0051 static u32 word_sum(void *words, int num)
0052 {
0053     u32 *p = words;
0054     u32 sum = 0;
0055 
0056     while (num--)
0057         sum += *p++;
0058 
0059     return sum;
0060 }
0061 
0062 static u32 word_sum_v2(u32 *p, u32 num)
0063 {
0064     u32 sum = 0;
0065     int i;
0066 
0067     for (i = 0; i < num; i++) {
0068         u32 val;
0069 
0070         val = p[i];
0071         if (val > ~sum)
0072             sum++;
0073         sum += val;
0074     }
0075     return ~sum;
0076 }
0077 
0078 static bool afs_is_v1(struct mtd_info *mtd, u_int off)
0079 {
0080     /* The magic is 12 bytes from the end of the erase block */
0081     u_int ptr = off + mtd->erasesize - 12;
0082     u32 magic;
0083     size_t sz;
0084     int ret;
0085 
0086     ret = mtd_read(mtd, ptr, 4, &sz, (u_char *)&magic);
0087     if (ret < 0) {
0088         printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
0089                ptr, ret);
0090         return false;
0091     }
0092     if (ret >= 0 && sz != 4)
0093         return false;
0094 
0095     return (magic == AFSV1_FOOTER_MAGIC);
0096 }
0097 
0098 static bool afs_is_v2(struct mtd_info *mtd, u_int off)
0099 {
0100     /* The magic is the 8 last bytes of the erase block */
0101     u_int ptr = off + mtd->erasesize - 8;
0102     u32 foot[2];
0103     size_t sz;
0104     int ret;
0105 
0106     ret = mtd_read(mtd, ptr, 8, &sz, (u_char *)foot);
0107     if (ret < 0) {
0108         printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
0109                ptr, ret);
0110         return false;
0111     }
0112     if (ret >= 0 && sz != 8)
0113         return false;
0114 
0115     return (foot[0] == AFSV2_FOOTER_MAGIC1 &&
0116         foot[1] == AFSV2_FOOTER_MAGIC2);
0117 }
0118 
0119 static int afs_parse_v1_partition(struct mtd_info *mtd,
0120                   u_int off, struct mtd_partition *part)
0121 {
0122     struct footer_v1 fs;
0123     struct image_info_v1 iis;
0124     u_int mask;
0125     /*
0126      * Static checks cannot see that we bail out if we have an error
0127      * reading the footer.
0128      */
0129     u_int iis_ptr;
0130     u_int img_ptr;
0131     u_int ptr;
0132     size_t sz;
0133     int ret;
0134     int i;
0135 
0136     /*
0137      * This is the address mask; we use this to mask off out of
0138      * range address bits.
0139      */
0140     mask = mtd->size - 1;
0141 
0142     ptr = off + mtd->erasesize - sizeof(fs);
0143     ret = mtd_read(mtd, ptr, sizeof(fs), &sz, (u_char *)&fs);
0144     if (ret >= 0 && sz != sizeof(fs))
0145         ret = -EINVAL;
0146     if (ret < 0) {
0147         printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
0148                ptr, ret);
0149         return ret;
0150     }
0151     /*
0152      * Check the checksum.
0153      */
0154     if (word_sum(&fs, sizeof(fs) / sizeof(u32)) != 0xffffffff)
0155         return -EINVAL;
0156 
0157     /*
0158      * Hide the SIB (System Information Block)
0159      */
0160     if (fs.type == 2)
0161         return 0;
0162 
0163     iis_ptr = fs.image_info_base & mask;
0164     img_ptr = fs.image_start & mask;
0165 
0166     /*
0167      * Check the image info base.  This can not
0168      * be located after the footer structure.
0169      */
0170     if (iis_ptr >= ptr)
0171         return 0;
0172 
0173     /*
0174      * Check the start of this image.  The image
0175      * data can not be located after this block.
0176      */
0177     if (img_ptr > off)
0178         return 0;
0179 
0180     /* Read the image info block */
0181     memset(&iis, 0, sizeof(iis));
0182     ret = mtd_read(mtd, iis_ptr, sizeof(iis), &sz, (u_char *)&iis);
0183     if (ret < 0) {
0184         printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
0185                iis_ptr, ret);
0186         return -EINVAL;
0187     }
0188 
0189     if (sz != sizeof(iis))
0190         return -EINVAL;
0191 
0192     /*
0193      * Validate the name - it must be NUL terminated.
0194      */
0195     for (i = 0; i < sizeof(iis.name); i++)
0196         if (iis.name[i] == '\0')
0197             break;
0198     if (i > sizeof(iis.name))
0199         return -EINVAL;
0200 
0201     part->name = kstrdup(iis.name, GFP_KERNEL);
0202     if (!part->name)
0203         return -ENOMEM;
0204 
0205     part->size = (iis.length + mtd->erasesize - 1) & ~(mtd->erasesize - 1);
0206     part->offset = img_ptr;
0207     part->mask_flags = 0;
0208 
0209     printk("  mtd: at 0x%08x, %5lluKiB, %8u, %s\n",
0210            img_ptr, part->size / 1024,
0211            iis.imageNumber, part->name);
0212 
0213     return 0;
0214 }
0215 
0216 static int afs_parse_v2_partition(struct mtd_info *mtd,
0217                   u_int off, struct mtd_partition *part)
0218 {
0219     u_int ptr;
0220     u32 footer[12];
0221     u32 imginfo[36];
0222     char *name;
0223     u32 version;
0224     u32 entrypoint;
0225     u32 attributes;
0226     u32 region_count;
0227     u32 block_start;
0228     u32 block_end;
0229     u32 crc;
0230     size_t sz;
0231     int ret;
0232     int i;
0233     int pad = 0;
0234 
0235     pr_debug("Parsing v2 partition @%08x-%08x\n",
0236          off, off + mtd->erasesize);
0237 
0238     /* First read the footer */
0239     ptr = off + mtd->erasesize - sizeof(footer);
0240     ret = mtd_read(mtd, ptr, sizeof(footer), &sz, (u_char *)footer);
0241     if ((ret < 0) || (ret >= 0 && sz != sizeof(footer))) {
0242         pr_err("AFS: mtd read failed at 0x%x: %d\n",
0243                ptr, ret);
0244         return -EIO;
0245     }
0246     name = (char *) &footer[0];
0247     version = footer[9];
0248     ptr = off + mtd->erasesize - sizeof(footer) - footer[8];
0249 
0250     pr_debug("found image \"%s\", version %08x, info @%08x\n",
0251          name, version, ptr);
0252 
0253     /* Then read the image information */
0254     ret = mtd_read(mtd, ptr, sizeof(imginfo), &sz, (u_char *)imginfo);
0255     if ((ret < 0) || (ret >= 0 && sz != sizeof(imginfo))) {
0256         pr_err("AFS: mtd read failed at 0x%x: %d\n",
0257                ptr, ret);
0258         return -EIO;
0259     }
0260 
0261     /* 32bit platforms have 4 bytes padding */
0262     crc = word_sum_v2(&imginfo[1], 34);
0263     if (!crc) {
0264         pr_debug("Padding 1 word (4 bytes)\n");
0265         pad = 1;
0266     } else {
0267         /* 64bit platforms have 8 bytes padding */
0268         crc = word_sum_v2(&imginfo[2], 34);
0269         if (!crc) {
0270             pr_debug("Padding 2 words (8 bytes)\n");
0271             pad = 2;
0272         }
0273     }
0274     if (crc) {
0275         pr_err("AFS: bad checksum on v2 image info: %08x\n", crc);
0276         return -EINVAL;
0277     }
0278     entrypoint = imginfo[pad];
0279     attributes = imginfo[pad+1];
0280     region_count = imginfo[pad+2];
0281     block_start = imginfo[20];
0282     block_end = imginfo[21];
0283 
0284     pr_debug("image entry=%08x, attr=%08x, regions=%08x, "
0285          "bs=%08x, be=%08x\n",
0286          entrypoint, attributes, region_count,
0287          block_start, block_end);
0288 
0289     for (i = 0; i < region_count; i++) {
0290         u32 region_load_addr = imginfo[pad + 3 + i*4];
0291         u32 region_size = imginfo[pad + 4 + i*4];
0292         u32 region_offset = imginfo[pad + 5 + i*4];
0293         u32 region_start;
0294         u32 region_end;
0295 
0296         pr_debug("  region %d: address: %08x, size: %08x, "
0297              "offset: %08x\n",
0298              i,
0299              region_load_addr,
0300              region_size,
0301              region_offset);
0302 
0303         region_start = off + region_offset;
0304         region_end = region_start + region_size;
0305         /* Align partition to end of erase block */
0306         region_end += (mtd->erasesize - 1);
0307         region_end &= ~(mtd->erasesize -1);
0308         pr_debug("   partition start = %08x, partition end = %08x\n",
0309              region_start, region_end);
0310 
0311         /* Create one partition per region */
0312         part->name = kstrdup(name, GFP_KERNEL);
0313         if (!part->name)
0314             return -ENOMEM;
0315         part->offset = region_start;
0316         part->size = region_end - region_start;
0317         part->mask_flags = 0;
0318     }
0319 
0320     return 0;
0321 }
0322 
0323 static int parse_afs_partitions(struct mtd_info *mtd,
0324                 const struct mtd_partition **pparts,
0325                 struct mtd_part_parser_data *data)
0326 {
0327     struct mtd_partition *parts;
0328     u_int off, sz;
0329     int ret = 0;
0330     int i;
0331 
0332     /* Count the partitions by looping over all erase blocks */
0333     for (i = off = sz = 0; off < mtd->size; off += mtd->erasesize) {
0334         if (afs_is_v1(mtd, off)) {
0335             sz += sizeof(struct mtd_partition);
0336             i += 1;
0337         }
0338         if (afs_is_v2(mtd, off)) {
0339             sz += sizeof(struct mtd_partition);
0340             i += 1;
0341         }
0342     }
0343 
0344     if (!i)
0345         return 0;
0346 
0347     parts = kzalloc(sz, GFP_KERNEL);
0348     if (!parts)
0349         return -ENOMEM;
0350 
0351     /*
0352      * Identify the partitions
0353      */
0354     for (i = off = 0; off < mtd->size; off += mtd->erasesize) {
0355         if (afs_is_v1(mtd, off)) {
0356             ret = afs_parse_v1_partition(mtd, off, &parts[i]);
0357             if (ret)
0358                 goto out_free_parts;
0359             i++;
0360         }
0361         if (afs_is_v2(mtd, off)) {
0362             ret = afs_parse_v2_partition(mtd, off, &parts[i]);
0363             if (ret)
0364                 goto out_free_parts;
0365             i++;
0366         }
0367     }
0368 
0369     *pparts = parts;
0370     return i;
0371 
0372 out_free_parts:
0373     while (--i >= 0)
0374         kfree(parts[i].name);
0375     kfree(parts);
0376     *pparts = NULL;
0377     return ret;
0378 }
0379 
0380 static const struct of_device_id mtd_parser_afs_of_match_table[] = {
0381     { .compatible = "arm,arm-firmware-suite" },
0382     {},
0383 };
0384 MODULE_DEVICE_TABLE(of, mtd_parser_afs_of_match_table);
0385 
0386 static struct mtd_part_parser afs_parser = {
0387     .parse_fn = parse_afs_partitions,
0388     .name = "afs",
0389     .of_match_table = mtd_parser_afs_of_match_table,
0390 };
0391 module_mtd_part_parser(afs_parser);
0392 
0393 MODULE_AUTHOR("ARM Ltd");
0394 MODULE_DESCRIPTION("ARM Firmware Suite partition parser");
0395 MODULE_LICENSE("GPL");