cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

afs.c (9332B)


      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
     30struct 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
     38struct 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
     51static 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
     62static 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
     78static 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
     98static 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
    119static 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
    216static 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	/* First read the footer */
    239	ptr = off + mtd->erasesize - sizeof(footer);
    240	ret = mtd_read(mtd, ptr, sizeof(footer), &sz, (u_char *)footer);
    241	if ((ret < 0) || (ret >= 0 && sz != sizeof(footer))) {
    242		pr_err("AFS: mtd read failed at 0x%x: %d\n",
    243		       ptr, ret);
    244		return -EIO;
    245	}
    246	name = (char *) &footer[0];
    247	version = footer[9];
    248	ptr = off + mtd->erasesize - sizeof(footer) - footer[8];
    249
    250	pr_debug("found image \"%s\", version %08x, info @%08x\n",
    251		 name, version, ptr);
    252
    253	/* Then read the image information */
    254	ret = mtd_read(mtd, ptr, sizeof(imginfo), &sz, (u_char *)imginfo);
    255	if ((ret < 0) || (ret >= 0 && sz != sizeof(imginfo))) {
    256		pr_err("AFS: mtd read failed at 0x%x: %d\n",
    257		       ptr, ret);
    258		return -EIO;
    259	}
    260
    261	/* 32bit platforms have 4 bytes padding */
    262	crc = word_sum_v2(&imginfo[1], 34);
    263	if (!crc) {
    264		pr_debug("Padding 1 word (4 bytes)\n");
    265		pad = 1;
    266	} else {
    267		/* 64bit platforms have 8 bytes padding */
    268		crc = word_sum_v2(&imginfo[2], 34);
    269		if (!crc) {
    270			pr_debug("Padding 2 words (8 bytes)\n");
    271			pad = 2;
    272		}
    273	}
    274	if (crc) {
    275		pr_err("AFS: bad checksum on v2 image info: %08x\n", crc);
    276		return -EINVAL;
    277	}
    278	entrypoint = imginfo[pad];
    279	attributes = imginfo[pad+1];
    280	region_count = imginfo[pad+2];
    281	block_start = imginfo[20];
    282	block_end = imginfo[21];
    283
    284	pr_debug("image entry=%08x, attr=%08x, regions=%08x, "
    285		 "bs=%08x, be=%08x\n",
    286		 entrypoint, attributes, region_count,
    287		 block_start, block_end);
    288
    289	for (i = 0; i < region_count; i++) {
    290		u32 region_load_addr = imginfo[pad + 3 + i*4];
    291		u32 region_size = imginfo[pad + 4 + i*4];
    292		u32 region_offset = imginfo[pad + 5 + i*4];
    293		u32 region_start;
    294		u32 region_end;
    295
    296		pr_debug("  region %d: address: %08x, size: %08x, "
    297			 "offset: %08x\n",
    298			 i,
    299			 region_load_addr,
    300			 region_size,
    301			 region_offset);
    302
    303		region_start = off + region_offset;
    304		region_end = region_start + region_size;
    305		/* Align partition to end of erase block */
    306		region_end += (mtd->erasesize - 1);
    307		region_end &= ~(mtd->erasesize -1);
    308		pr_debug("   partition start = %08x, partition end = %08x\n",
    309			 region_start, region_end);
    310
    311		/* Create one partition per region */
    312		part->name = kstrdup(name, GFP_KERNEL);
    313		if (!part->name)
    314			return -ENOMEM;
    315		part->offset = region_start;
    316		part->size = region_end - region_start;
    317		part->mask_flags = 0;
    318	}
    319
    320	return 0;
    321}
    322
    323static int parse_afs_partitions(struct mtd_info *mtd,
    324				const struct mtd_partition **pparts,
    325				struct mtd_part_parser_data *data)
    326{
    327	struct mtd_partition *parts;
    328	u_int off, sz;
    329	int ret = 0;
    330	int i;
    331
    332	/* Count the partitions by looping over all erase blocks */
    333	for (i = off = sz = 0; off < mtd->size; off += mtd->erasesize) {
    334		if (afs_is_v1(mtd, off)) {
    335			sz += sizeof(struct mtd_partition);
    336			i += 1;
    337		}
    338		if (afs_is_v2(mtd, off)) {
    339			sz += sizeof(struct mtd_partition);
    340			i += 1;
    341		}
    342	}
    343
    344	if (!i)
    345		return 0;
    346
    347	parts = kzalloc(sz, GFP_KERNEL);
    348	if (!parts)
    349		return -ENOMEM;
    350
    351	/*
    352	 * Identify the partitions
    353	 */
    354	for (i = off = 0; off < mtd->size; off += mtd->erasesize) {
    355		if (afs_is_v1(mtd, off)) {
    356			ret = afs_parse_v1_partition(mtd, off, &parts[i]);
    357			if (ret)
    358				goto out_free_parts;
    359			i++;
    360		}
    361		if (afs_is_v2(mtd, off)) {
    362			ret = afs_parse_v2_partition(mtd, off, &parts[i]);
    363			if (ret)
    364				goto out_free_parts;
    365			i++;
    366		}
    367	}
    368
    369	*pparts = parts;
    370	return i;
    371
    372out_free_parts:
    373	while (--i >= 0)
    374		kfree(parts[i].name);
    375	kfree(parts);
    376	*pparts = NULL;
    377	return ret;
    378}
    379
    380static const struct of_device_id mtd_parser_afs_of_match_table[] = {
    381	{ .compatible = "arm,arm-firmware-suite" },
    382	{},
    383};
    384MODULE_DEVICE_TABLE(of, mtd_parser_afs_of_match_table);
    385
    386static struct mtd_part_parser afs_parser = {
    387	.parse_fn = parse_afs_partitions,
    388	.name = "afs",
    389	.of_match_table = mtd_parser_afs_of_match_table,
    390};
    391module_mtd_part_parser(afs_parser);
    392
    393MODULE_AUTHOR("ARM Ltd");
    394MODULE_DESCRIPTION("ARM Firmware Suite partition parser");
    395MODULE_LICENSE("GPL");