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