0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
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
0028 #define AFSV2_FOOTER_MAGIC2 0x464F4F54
0029
0030 struct footer_v1 {
0031 u32 image_info_base;
0032 u32 image_start;
0033 u32 signature;
0034 u32 type;
0035 u32 checksum;
0036 };
0037
0038 struct image_info_v1 {
0039 u32 bootFlags;
0040 u32 imageNumber;
0041 u32 loadAddress;
0042 u32 length;
0043 u32 address;
0044 char name[16];
0045 u32 headerBase;
0046 u32 header_length;
0047 u32 headerType;
0048 u32 checksum;
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
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
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
0127
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
0138
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
0153
0154 if (word_sum(&fs, sizeof(fs) / sizeof(u32)) != 0xffffffff)
0155 return -EINVAL;
0156
0157
0158
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
0168
0169
0170 if (iis_ptr >= ptr)
0171 return 0;
0172
0173
0174
0175
0176
0177 if (img_ptr > off)
0178 return 0;
0179
0180
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
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
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
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
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
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
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
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
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
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");