1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /*====================================================================== 3 4 drivers/mtd/afs.c: ARM Flash Layout/Partitioning 5 6 Copyright © 2000 ARM Limited 7 Copyright (C) 2019 Linus Walleij 8 9 10 This is access code for flashes using ARM's flash partitioning 11 standards. 12 13 ======================================================================*/ 14 15 #include <linux/module.h> 16 #include <linux/types.h> 17 #include <linux/kernel.h> 18 #include <linux/slab.h> 19 #include <linux/string.h> 20 #include <linux/init.h> 21 22 #include <linux/mtd/mtd.h> 23 #include <linux/mtd/map.h> 24 #include <linux/mtd/partitions.h> 25 26 #define AFSV1_FOOTER_MAGIC 0xA0FFFF9F 27 #define AFSV2_FOOTER_MAGIC1 0x464C5348 /* "FLSH" */ 28 #define AFSV2_FOOTER_MAGIC2 0x464F4F54 /* "FOOT" */ 29 30 struct footer_v1 { 31 u32 image_info_base; /* Address of first word of ImageFooter */ 32 u32 image_start; /* Start of area reserved by this footer */ 33 u32 signature; /* 'Magic' number proves it's a footer */ 34 u32 type; /* Area type: ARM Image, SIB, customer */ 35 u32 checksum; /* Just this structure */ 36 }; 37 38 struct image_info_v1 { 39 u32 bootFlags; /* Boot flags, compression etc. */ 40 u32 imageNumber; /* Unique number, selects for boot etc. */ 41 u32 loadAddress; /* Address program should be loaded to */ 42 u32 length; /* Actual size of image */ 43 u32 address; /* Image is executed from here */ 44 char name[16]; /* Null terminated */ 45 u32 headerBase; /* Flash Address of any stripped header */ 46 u32 header_length; /* Length of header in memory */ 47 u32 headerType; /* AIF, RLF, s-record etc. */ 48 u32 checksum; /* Image checksum (inc. this struct) */ 49 }; 50 51 static u32 word_sum(void *words, int num) 52 { 53 u32 *p = words; 54 u32 sum = 0; 55 56 while (num--) 57 sum += *p++; 58 59 return sum; 60 } 61 62 static u32 word_sum_v2(u32 *p, u32 num) 63 { 64 u32 sum = 0; 65 int i; 66 67 for (i = 0; i < num; i++) { 68 u32 val; 69 70 val = p[i]; 71 if (val > ~sum) 72 sum++; 73 sum += val; 74 } 75 return ~sum; 76 } 77 78 static bool afs_is_v1(struct mtd_info *mtd, u_int off) 79 { 80 /* The magic is 12 bytes from the end of the erase block */ 81 u_int ptr = off + mtd->erasesize - 12; 82 u32 magic; 83 size_t sz; 84 int ret; 85 86 ret = mtd_read(mtd, ptr, 4, &sz, (u_char *)&magic); 87 if (ret < 0) { 88 printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n", 89 ptr, ret); 90 return false; 91 } 92 if (ret >= 0 && sz != 4) 93 return false; 94 95 return (magic == AFSV1_FOOTER_MAGIC); 96 } 97 98 static bool afs_is_v2(struct mtd_info *mtd, u_int off) 99 { 100 /* The magic is the 8 last bytes of the erase block */ 101 u_int ptr = off + mtd->erasesize - 8; 102 u32 foot[2]; 103 size_t sz; 104 int ret; 105 106 ret = mtd_read(mtd, ptr, 8, &sz, (u_char *)foot); 107 if (ret < 0) { 108 printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n", 109 ptr, ret); 110 return false; 111 } 112 if (ret >= 0 && sz != 8) 113 return false; 114 115 return (foot[0] == AFSV2_FOOTER_MAGIC1 && 116 foot[1] == AFSV2_FOOTER_MAGIC2); 117 } 118 119 static int afs_parse_v1_partition(struct mtd_info *mtd, 120 u_int off, struct mtd_partition *part) 121 { 122 struct footer_v1 fs; 123 struct image_info_v1 iis; 124 u_int mask; 125 /* 126 * Static checks cannot see that we bail out if we have an error 127 * reading the footer. 128 */ 129 u_int iis_ptr; 130 u_int img_ptr; 131 u_int ptr; 132 size_t sz; 133 int ret; 134 int i; 135 136 /* 137 * This is the address mask; we use this to mask off out of 138 * range address bits. 139 */ 140 mask = mtd->size - 1; 141 142 ptr = off + mtd->erasesize - sizeof(fs); 143 ret = mtd_read(mtd, ptr, sizeof(fs), &sz, (u_char *)&fs); 144 if (ret >= 0 && sz != sizeof(fs)) 145 ret = -EINVAL; 146 if (ret < 0) { 147 printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n", 148 ptr, ret); 149 return ret; 150 } 151 /* 152 * Check the checksum. 153 */ 154 if (word_sum(&fs, sizeof(fs) / sizeof(u32)) != 0xffffffff) 155 return -EINVAL; 156 157 /* 158 * Hide the SIB (System Information Block) 159 */ 160 if (fs.type == 2) 161 return 0; 162 163 iis_ptr = fs.image_info_base & mask; 164 img_ptr = fs.image_start & mask; 165 166 /* 167 * Check the image info base. This can not 168 * be located after the footer structure. 169 */ 170 if (iis_ptr >= ptr) 171 return 0; 172 173 /* 174 * Check the start of this image. The image 175 * data can not be located after this block. 176 */ 177 if (img_ptr > off) 178 return 0; 179 180 /* Read the image info block */ 181 memset(&iis, 0, sizeof(iis)); 182 ret = mtd_read(mtd, iis_ptr, sizeof(iis), &sz, (u_char *)&iis); 183 if (ret < 0) { 184 printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n", 185 iis_ptr, ret); 186 return -EINVAL; 187 } 188 189 if (sz != sizeof(iis)) 190 return -EINVAL; 191 192 /* 193 * Validate the name - it must be NUL terminated. 194 */ 195 for (i = 0; i < sizeof(iis.name); i++) 196 if (iis.name[i] == '\0') 197 break; 198 if (i > sizeof(iis.name)) 199 return -EINVAL; 200 201 part->name = kstrdup(iis.name, GFP_KERNEL); 202 if (!part->name) 203 return -ENOMEM; 204 205 part->size = (iis.length + mtd->erasesize - 1) & ~(mtd->erasesize - 1); 206 part->offset = img_ptr; 207 part->mask_flags = 0; 208 209 printk(" mtd: at 0x%08x, %5lluKiB, %8u, %s\n", 210 img_ptr, part->size / 1024, 211 iis.imageNumber, part->name); 212 213 return 0; 214 } 215 216 static int afs_parse_v2_partition(struct mtd_info *mtd, 217 u_int off, struct mtd_partition *part) 218 { 219 u_int ptr; 220 u32 footer[12]; 221 u32 imginfo[36]; 222 char *name; 223 u32 version; 224 u32 entrypoint; 225 u32 attributes; 226 u32 region_count; 227 u32 block_start; 228 u32 block_end; 229 u32 crc; 230 size_t sz; 231 int ret; 232 int i; 233 int pad = 0; 234 235 pr_debug("Parsing v2 partition @%08x-%08x\n", 236 off, off + mtd->erasesize); 237 238 if (mtd->erasesize < sizeof(footer)) 239 return -EINVAL; 240 241 /* First read the footer */ 242 ptr = off + mtd->erasesize - sizeof(footer); 243 ret = mtd_read(mtd, ptr, sizeof(footer), &sz, (u_char *)footer); 244 if ((ret < 0) || (ret >= 0 && sz != sizeof(footer))) { 245 pr_err("AFS: mtd read failed at 0x%x: %d\n", 246 ptr, ret); 247 return -EIO; 248 } 249 name = (char *) &footer[0]; 250 version = footer[9]; 251 if (footer[8] > mtd->erasesize - sizeof(footer)) 252 return -EINVAL; 253 ptr = off + mtd->erasesize - sizeof(footer) - footer[8]; 254 255 pr_debug("found image \"%s\", version %08x, info @%08x\n", 256 name, version, ptr); 257 258 /* Then read the image information */ 259 ret = mtd_read(mtd, ptr, sizeof(imginfo), &sz, (u_char *)imginfo); 260 if ((ret < 0) || (ret >= 0 && sz != sizeof(imginfo))) { 261 pr_err("AFS: mtd read failed at 0x%x: %d\n", 262 ptr, ret); 263 return -EIO; 264 } 265 266 /* 32bit platforms have 4 bytes padding */ 267 crc = word_sum_v2(&imginfo[1], 34); 268 if (!crc) { 269 pr_debug("Padding 1 word (4 bytes)\n"); 270 pad = 1; 271 } else { 272 /* 64bit platforms have 8 bytes padding */ 273 crc = word_sum_v2(&imginfo[2], 34); 274 if (!crc) { 275 pr_debug("Padding 2 words (8 bytes)\n"); 276 pad = 2; 277 } 278 } 279 if (crc) { 280 pr_err("AFS: bad checksum on v2 image info: %08x\n", crc); 281 return -EINVAL; 282 } 283 entrypoint = imginfo[pad]; 284 attributes = imginfo[pad+1]; 285 region_count = imginfo[pad+2]; 286 if (region_count > (ARRAY_SIZE(imginfo) - pad - 3) / 4) 287 return -EINVAL; 288 block_start = imginfo[20]; 289 block_end = imginfo[21]; 290 291 pr_debug("image entry=%08x, attr=%08x, regions=%08x, " 292 "bs=%08x, be=%08x\n", 293 entrypoint, attributes, region_count, 294 block_start, block_end); 295 296 for (i = 0; i < region_count; i++) { 297 u32 region_load_addr = imginfo[pad + 3 + i*4]; 298 u32 region_size = imginfo[pad + 4 + i*4]; 299 u32 region_offset = imginfo[pad + 5 + i*4]; 300 u32 region_start; 301 u32 region_end; 302 303 pr_debug(" region %d: address: %08x, size: %08x, " 304 "offset: %08x\n", 305 i, 306 region_load_addr, 307 region_size, 308 region_offset); 309 310 region_start = off + region_offset; 311 region_end = region_start + region_size; 312 /* Align partition to end of erase block */ 313 region_end += (mtd->erasesize - 1); 314 region_end &= ~(mtd->erasesize -1); 315 pr_debug(" partition start = %08x, partition end = %08x\n", 316 region_start, region_end); 317 318 /* Create one partition per region */ 319 part->name = kstrdup(name, GFP_KERNEL); 320 if (!part->name) 321 return -ENOMEM; 322 part->offset = region_start; 323 part->size = region_end - region_start; 324 part->mask_flags = 0; 325 } 326 327 return 0; 328 } 329 330 static int parse_afs_partitions(struct mtd_info *mtd, 331 const struct mtd_partition **pparts, 332 struct mtd_part_parser_data *data) 333 { 334 struct mtd_partition *parts; 335 u_int off, sz; 336 int ret = 0; 337 int i; 338 339 /* Count the partitions by looping over all erase blocks */ 340 for (i = off = sz = 0; off < mtd->size; off += mtd->erasesize) { 341 if (afs_is_v1(mtd, off)) { 342 sz += sizeof(struct mtd_partition); 343 i += 1; 344 } 345 if (afs_is_v2(mtd, off)) { 346 sz += sizeof(struct mtd_partition); 347 i += 1; 348 } 349 } 350 351 if (!i) 352 return 0; 353 354 parts = kzalloc(sz, GFP_KERNEL); 355 if (!parts) 356 return -ENOMEM; 357 358 /* 359 * Identify the partitions 360 */ 361 for (i = off = 0; off < mtd->size; off += mtd->erasesize) { 362 if (afs_is_v1(mtd, off)) { 363 ret = afs_parse_v1_partition(mtd, off, &parts[i]); 364 if (ret) 365 goto out_free_parts; 366 i++; 367 } 368 if (afs_is_v2(mtd, off)) { 369 ret = afs_parse_v2_partition(mtd, off, &parts[i]); 370 if (ret) 371 goto out_free_parts; 372 i++; 373 } 374 } 375 376 *pparts = parts; 377 return i; 378 379 out_free_parts: 380 while (--i >= 0) 381 kfree(parts[i].name); 382 kfree(parts); 383 *pparts = NULL; 384 return ret; 385 } 386 387 static const struct of_device_id mtd_parser_afs_of_match_table[] = { 388 { .compatible = "arm,arm-firmware-suite" }, 389 {}, 390 }; 391 MODULE_DEVICE_TABLE(of, mtd_parser_afs_of_match_table); 392 393 static struct mtd_part_parser afs_parser = { 394 .parse_fn = parse_afs_partitions, 395 .name = "afs", 396 .of_match_table = mtd_parser_afs_of_match_table, 397 }; 398 module_mtd_part_parser(afs_parser); 399 400 MODULE_AUTHOR("ARM Ltd"); 401 MODULE_DESCRIPTION("ARM Firmware Suite partition parser"); 402 MODULE_LICENSE("GPL"); 403