xref: /rk3399_rockchip-uboot/arch/arm/mach-rockchip/resource_img.c (revision 1ca40e22ee9380e83a4a6ec1ff5abdb53d9d2cab)
1 /*
2  * (C) Copyright 2017 Rockchip Electronics Co., Ltd
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6 #include <common.h>
7 #include <adc.h>
8 #include <android_ab.h>
9 #include <android_bootloader.h>
10 #include <android_image.h>
11 #include <boot_rkimg.h>
12 #include <bmp_layout.h>
13 #include <malloc.h>
14 #include <asm/io.h>
15 #include <asm/unaligned.h>
16 #include <dm/ofnode.h>
17 #include <linux/list.h>
18 #include <asm/arch/fit.h>
19 #include <asm/arch/uimage.h>
20 #include <asm/arch/resource_img.h>
21 
22 DECLARE_GLOBAL_DATA_PTR;
23 
24 #define PART_RESOURCE			"resource"
25 #define RESOURCE_MAGIC			"RSCE"
26 #define RESOURCE_MAGIC_SIZE		4
27 #define RESOURCE_VERSION		0
28 #define CONTENT_VERSION			0
29 #define ENTRY_TAG			"ENTR"
30 #define ENTRY_TAG_SIZE			4
31 #define MAX_FILE_NAME_LEN		220
32 #define MAX_HASH_LEN			32
33 #define DEFAULT_DTB_FILE		"rk-kernel.dtb"
34 
35 /*
36  *         resource image structure
37  * ----------------------------------------------
38  * |                                            |
39  * |    header  (1 block)                       |
40  * |                                            |
41  * ---------------------------------------------|
42  * |                      |                     |
43  * |    entry0  (1 block) |                     |
44  * |                      |                     |
45  * ------------------------                     |
46  * |                      |                     |
47  * |    entry1  (1 block) | contents (n blocks) |
48  * |                      |                     |
49  * ------------------------                     |
50  * |    ......            |                     |
51  * ------------------------                     |
52  * |                      |                     |
53  * |    entryn  (1 block) |                     |
54  * |                      |                     |
55  * ----------------------------------------------
56  * |                                            |
57  * |    file0  (x blocks)                       |
58  * |                                            |
59  * ----------------------------------------------
60  * |                                            |
61  * |    file1  (y blocks)                       |
62  * |                                            |
63  * ----------------------------------------------
64  * |                   ......                   |
65  * |---------------------------------------------
66  * |                                            |
67  * |    filen  (z blocks)                       |
68  * |                                            |
69  * ----------------------------------------------
70  */
71 
72 /**
73  * struct resource_image_header
74  *
75  * @magic: should be "RSCE"
76  * @version: resource image version, current is 0
77  * @c_version: content version, current is 0
78  * @blks: the size of the header ( 1 block = 512 bytes)
79  * @c_offset: contents offset(by block) in the image
80  * @e_blks: the size(by block) of the entry in the contents
81  * @e_num: numbers of the entrys.
82  */
83 
84 struct resource_img_hdr {
85 	char		magic[4];
86 	uint16_t	version;
87 	uint16_t	c_version;
88 	uint8_t		blks;
89 	uint8_t		c_offset;
90 	uint8_t		e_blks;
91 	uint32_t	e_nums;
92 };
93 
94 struct resource_entry {
95 	char		tag[4];
96 	char		name[MAX_FILE_NAME_LEN];
97 	char		hash[MAX_HASH_LEN];
98 	uint32_t	hash_size;
99 	uint32_t	f_offset;	/* Sector offset */
100 	uint32_t	f_size;		/* Bytes */
101 };
102 
103 LIST_HEAD(entrys_head);
104 LIST_HEAD(entrys_dtbs_head);
105 
106 __weak int board_resource_dtb_accepted(char *dtb_name)
107 {
108 	return 1;
109 }
110 
111 int resource_image_check_header(void *rsce_hdr)
112 {
113 	struct resource_img_hdr *hdr = rsce_hdr;
114 	int ret;
115 
116 	ret = memcmp(RESOURCE_MAGIC, hdr->magic, RESOURCE_MAGIC_SIZE);
117 	if (ret) {
118 		debug("bad resource image magic: %s\n",
119 		      hdr->magic ? hdr->magic : "none");
120 		ret = -EINVAL;
121 	}
122 
123 	debug("resource image header:\n");
124 	debug("magic:%s\n", hdr->magic);
125 	debug("version:%d\n", hdr->version);
126 	debug("c_version:%d\n", hdr->c_version);
127 	debug("blks:%d\n", hdr->blks);
128 	debug("c_offset:%d\n", hdr->c_offset);
129 	debug("e_blks:%d\n", hdr->e_blks);
130 	debug("e_num:%d\n", hdr->e_nums);
131 
132 	return ret;
133 }
134 
135 static int add_file_to_list(struct resource_entry *entry, int rsce_base, bool ram)
136 {
137 	struct resource_file *file;
138 
139 	if (memcmp(entry->tag, ENTRY_TAG, ENTRY_TAG_SIZE)) {
140 		debug("invalid entry tag\n");
141 		return -ENOENT;
142 	}
143 
144 	file = malloc(sizeof(*file));
145 	if (!file) {
146 		debug("out of memory\n");
147 		return -ENOMEM;
148 	}
149 
150 	strcpy(file->name, entry->name);
151 	file->rsce_base = rsce_base;
152 	file->f_offset = entry->f_offset;
153 	file->f_size = entry->f_size;
154 	file->hash_size = entry->hash_size;
155 	file->ram = ram;
156 	memcpy(file->hash, entry->hash, entry->hash_size);
157 	list_add_tail(&file->link, &entrys_head);
158 	if (strstr(file->name, DTB_SUFFIX) && board_resource_dtb_accepted(file->name))
159 		list_add_tail(&file->dtbs, &entrys_dtbs_head);
160 	debug("ENTRY: addr: %p, name: %18s, base: 0x%08x, offset: 0x%08x, size: 0x%08x\n",
161 	      entry, file->name, file->rsce_base, file->f_offset, file->f_size);
162 
163 	return 0;
164 }
165 
166 int resource_replace_entry(const char *f_name, uint32_t base,
167 			   uint32_t f_offset, uint32_t f_size)
168 {
169 	struct resource_entry *entry;
170 	struct resource_file *file;
171 	struct list_head *node;
172 
173 	if (!f_name || !f_size)
174 		return -EINVAL;
175 
176 	entry = calloc(1, sizeof(*entry));
177 	if (!entry)
178 		return -ENOMEM;
179 
180 	strcpy(entry->tag, ENTRY_TAG);
181 	strcpy(entry->name, f_name);
182 	entry->f_offset = f_offset;
183 	entry->f_size = f_size;
184 	entry->hash_size = 0;
185 
186 	/* Delete exist entry, then add this new */
187 	list_for_each(node, &entrys_head) {
188 		file = list_entry(node, struct resource_file, link);
189 		if (!strcmp(file->name, entry->name)) {
190 			list_del(&file->link);
191 			list_del(&file->dtbs);
192 			free(file);
193 			break;
194 		}
195 	}
196 
197 	add_file_to_list(entry, base, false);
198 	free(entry);
199 
200 	return 0;
201 }
202 
203 int resource_create_ram_list(struct blk_desc *dev_desc, void *rsce_hdr)
204 {
205 	struct resource_img_hdr *hdr = rsce_hdr;
206 	struct resource_entry *entry;
207 	int e_num, size;
208 	void *data;
209 	int ret = 0;
210 
211 	if (resource_image_check_header(hdr)) {
212 		ret = -EINVAL;
213 		goto out;
214 	}
215 
216 	list_del_init(&entrys_head);
217 	list_del_init(&entrys_dtbs_head);
218 	data = (void *)((ulong)hdr + hdr->c_offset * dev_desc->blksz);
219 	for (e_num = 0; e_num < hdr->e_nums; e_num++) {
220 		size = e_num * hdr->e_blks * dev_desc->blksz;
221 		entry = (struct resource_entry *)(data + size);
222 		add_file_to_list(entry, (ulong)hdr, true);
223 	}
224 out:
225 	resource_read_logo_bmps();
226 
227 	return ret;
228 }
229 
230 static int resource_create_list(struct blk_desc *dev_desc, int rsce_base)
231 {
232 	struct resource_img_hdr *hdr;
233 	struct resource_entry *entry;
234 	int blknum, e_num;
235 	void *data = NULL;
236 	int ret = 0;
237 	int size;
238 
239 	hdr = memalign(ARCH_DMA_MINALIGN, dev_desc->blksz);
240 	if (!hdr)
241 		return -ENOMEM;
242 
243 	if (blk_dread(dev_desc, rsce_base, 1, hdr) != 1) {
244 		printf("Failed to read resource hdr\n");
245 		ret = -EIO;
246 		goto err;
247 	}
248 
249 	if (resource_image_check_header(hdr)) {
250 		if (fdt_check_header(hdr)) {
251 			printf("No valid resource or dtb file\n");
252 			ret = -EINVAL;
253 			goto err;
254 		} else {
255 			free(hdr);
256 			return resource_replace_entry(DEFAULT_DTB_FILE, rsce_base,
257 						      0, fdt_totalsize(hdr));
258 		}
259 	}
260 
261 	blknum = hdr->e_blks * hdr->e_nums;
262 	data = memalign(ARCH_DMA_MINALIGN, blknum * dev_desc->blksz);
263 	if (!data) {
264 		ret = -ENOMEM;
265 		goto err;
266 	}
267 
268 	if (blk_dread(dev_desc, rsce_base + hdr->c_offset,
269 		      blknum, data) != blknum) {
270 		printf("Failed to read resource entries\n");
271 		ret = -EIO;
272 		goto err;
273 	}
274 
275 	/*
276 	 * Add all file into resource file list, and load what we want from
277 	 * storage when we really need it.
278 	 */
279 	for (e_num = 0; e_num < hdr->e_nums; e_num++) {
280 		size = e_num * hdr->e_blks * dev_desc->blksz;
281 		entry = (struct resource_entry *)(data + size);
282 		add_file_to_list(entry, rsce_base, false);
283 	}
284 
285 err:
286 	if (data)
287 		free(data);
288 	if (hdr)
289 		free(hdr);
290 
291 	resource_read_logo_bmps();
292 
293 	return ret;
294 }
295 
296 static int read_dtb_from_android(struct blk_desc *dev_desc,
297 				 struct andr_img_hdr *hdr,
298 				 ulong rsce_base)
299 {
300 	ulong dtb_offset = 0;
301 	ulong dtb_size = 0;
302 
303 	if (!hdr || hdr->header_version <= 1) {
304 		return 0;
305 	} else if (hdr->header_version == 2) {
306 		dtb_offset += hdr->page_size;
307 		dtb_offset += ALIGN(hdr->kernel_size, hdr->page_size);
308 		dtb_offset += ALIGN(hdr->ramdisk_size, hdr->page_size);
309 		dtb_offset += ALIGN(hdr->recovery_dtbo_size, hdr->page_size) +
310 			      ALIGN(hdr->second_size, hdr->page_size);
311 		dtb_size = hdr->dtb_size;
312 	} else if (hdr->header_version >= 3) {
313 		ulong vendor_boot_hdr_size = (hdr->header_version == 3) ?
314 			VENDOR_BOOT_HDRv3_SIZE : VENDOR_BOOT_HDRv4_SIZE;
315 
316 		dtb_offset += ALIGN(vendor_boot_hdr_size,
317 				    hdr->vendor_page_size) +
318 			      ALIGN(hdr->vendor_ramdisk_size,
319 				    hdr->vendor_page_size);
320 		dtb_size = hdr->dtb_size;
321 	}
322 
323 	if (!dtb_size)
324 		return 0;
325 
326 	/*
327 	 * boot_img_hdr_v234 feature.
328 	 *
329 	 * If dtb position is present, replace the old with new one if
330 	 * we don't need to verify DTB hash from resource.img file entry.
331 	 */
332 	dtb_offset = DIV_ROUND_UP(dtb_offset, dev_desc->blksz);
333 #ifndef CONFIG_ROCKCHIP_DTB_VERIFY
334 	if (replace_resource_entry(DEFAULT_DTB_FILE, rsce_base, dtb_offset, dtb_size))
335 		printf("Failed to load dtb from v2 dtb position\n");
336 	else
337 #endif
338 		env_update("bootargs", "androidboot.dtb_idx=0");
339 
340 	return 0;
341 }
342 
343 static int get_resource_base_sector(struct blk_desc *dev_desc,
344 				    struct andr_img_hdr **ret_hdr)
345 {
346 	disk_partition_t part;
347 	int rsce_base = 0;
348 #ifdef CONFIG_ANDROID_BOOT_IMAGE
349 	struct andr_img_hdr *hdr;
350 	u32 os_ver = 0, os_lvl;
351 	const char *part_boot = PART_BOOT;
352 
353 	/*
354 	 * Anyway, we must read android hdr firstly from boot/recovery partition
355 	 * to get the 'os_version' for android_bcb_msg_sector_offset(), in order
356 	 * to confirm BCB message offset of *MISC* partition.
357 	 */
358 #ifdef CONFIG_ANDROID_AB
359 	part_boot = ab_can_find_recovery_part() ? PART_RECOVERY : PART_BOOT;
360 #endif
361 
362 	if (part_get_info_by_name(dev_desc, part_boot, &part) < 0)
363 		goto resource_part;
364 
365 	hdr = populate_andr_img_hdr(dev_desc, &part);
366 	if (hdr) {
367 		os_ver = hdr->os_version >> 11;
368 		os_lvl = hdr->os_version & ((1U << 11) - 1);
369 		if (os_ver)
370 			gd->bd->bi_andr_version = hdr->os_version;
371 	}
372 
373 #ifndef CONFIG_ANDROID_AB
374 	/* Get boot mode from misc and read if recovery mode */
375 	if (rockchip_get_boot_mode() == BOOT_MODE_RECOVERY) {
376 		if (hdr)
377 			free(hdr);
378 
379 		if (part_get_info_by_name(dev_desc, PART_RECOVERY, &part) < 0)
380 			goto resource_part;
381 
382 		hdr = populate_andr_img_hdr(dev_desc, &part);
383 		if (!hdr)
384 			goto resource_part;
385 	}
386 #endif
387 	/* If Android v012, getting resource from second position ! */
388 	if (hdr) {
389 		if (os_ver)
390 			printf("Android %u.%u, Build %u.%u, v%d\n",
391 			       (os_ver >> 14) & 0x7F, (os_ver >> 7) & 0x7F,
392 			       (os_lvl >> 4) + 2000, os_lvl & 0x0F,
393 			       hdr->header_version);
394 		*ret_hdr = hdr;
395 		if (hdr->header_version < 3) {
396 			rsce_base = part.start * dev_desc->blksz;
397 			rsce_base += hdr->page_size;
398 			rsce_base += ALIGN(hdr->kernel_size, hdr->page_size);
399 			rsce_base += ALIGN(hdr->ramdisk_size, hdr->page_size);
400 			rsce_base = DIV_ROUND_UP(rsce_base, dev_desc->blksz);
401 			goto finish;
402 		}
403 	}
404 resource_part:
405 #endif
406 	/* resource partition */
407 	if (part_get_info_by_name(dev_desc, PART_RESOURCE, &part) < 0) {
408 		printf("No resource partition\n");
409 		return -ENODEV;
410 	} else {
411 		rsce_base = part.start;
412 	}
413 #ifdef CONFIG_ANDROID_BOOT_IMAGE
414 finish:
415 #endif
416 	printf("Found DTB in %s part\n", part.name);
417 
418 	return rsce_base;
419 }
420 
421 /*
422  * There are: logo/battery pictures and dtb file in the resource image by default.
423  *
424  * This function does:
425  *
426  * 1. Get resource image base sector from: boot/recovery(AOSP) > resource(RK)
427  * 2. Create resource files list(addition: add logo bmps)
428  * 3. Add dtb from android v2 dtb pos, override the old one from resource file
429  */
430 int resource_init_list(void)
431 {
432 	struct andr_img_hdr *hdr = NULL;
433 	struct blk_desc *dev_desc;
434 	int rsce_base;
435 
436 	dev_desc = rockchip_get_bootdev();
437 	if (!dev_desc) {
438 		printf("No dev_desc!\n");
439 		return -ENODEV;
440 	}
441 
442 	rsce_base = get_resource_base_sector(dev_desc, &hdr);
443 	if (rsce_base > 0) {
444 		if (resource_create_list(dev_desc, rsce_base))
445 			printf("Failed to create resource list\n");
446 	}
447 
448 	/* override the resource dtb with android dtb if need */
449 	return read_dtb_from_android(dev_desc, hdr, rsce_base);
450 }
451 
452 int resource_is_empty(void)
453 {
454 	return list_empty(&entrys_head);
455 }
456 
457 static struct resource_file *get_file_info(const char *name)
458 {
459 	struct resource_file *file;
460 	struct list_head *node;
461 
462 	if (list_empty(&entrys_head)) {
463 		if (resource_init_list())
464 			return NULL;
465 	}
466 
467 	list_for_each(node, &entrys_head) {
468 		file = list_entry(node, struct resource_file, link);
469 		if (!strcmp(file->name, name))
470 			return file;
471 	}
472 
473 	return NULL;
474 }
475 
476 /*
477  * read file from resource partition
478  * @buf: destination buf to store file data;
479  * @name: file name
480  * @offset: blocks offset in the file, 1 block = 512 bytes
481  * @len: the size(by bytes) of file to read.
482  */
483 int rockchip_read_resource_file(void *buf, const char *name, int offset, int len)
484 {
485 	struct resource_file *file;
486 	struct blk_desc *dev_desc;
487 	int ret = 0;
488 	int blks;
489 	ulong src;
490 
491 	file = get_file_info(name);
492 	if (!file) {
493 		printf("No file: %s\n", name);
494 		return -ENOENT;
495 	}
496 
497 	dev_desc = rockchip_get_bootdev();
498 	if (!dev_desc) {
499 		printf("No dev_desc!\n");
500 		return -ENODEV;
501 	}
502 
503 	if (len <= 0 || len > file->f_size)
504 		len = file->f_size;
505 
506 	if (file->ram) {
507 		src = file->rsce_base +
508 			(file->f_offset + offset) * dev_desc->blksz;
509 		memcpy(buf, (char *)src, len);
510 		ret = len;
511 	} else {
512 		blks = DIV_ROUND_UP(len, dev_desc->blksz);
513 		ret = blk_dread(dev_desc,
514 				file->rsce_base + file->f_offset + offset,
515 				blks, buf);
516 		ret = (ret != blks) ? -EIO : len;
517 	}
518 
519 	return ret;
520 }
521 
522 static struct resource_file *get_default_dtb(void)
523 {
524 	struct resource_file *target_file = NULL;
525 	struct resource_file *file;
526 	struct list_head *node;
527 	int num = 0;
528 
529 	if (list_empty(&entrys_head)) {
530 		if (resource_init_list())
531 			return NULL;
532 	}
533 
534 	list_for_each(node, &entrys_dtbs_head) {
535 		num++;
536 		file = list_entry(node, struct resource_file, dtbs);
537 		if (strcmp(file->name, DEFAULT_DTB_FILE))
538 			target_file = file;
539 	}
540 
541 	/*
542 	 * two possible case:
543 	 *	case 1. rk-kernel.dtb only
544 	 *	case 2. targe_file(s) + rk-kernel.dtb(maybe they are the same),
545 	 *		use (last)target_file as result one.
546 	 */
547 	if (num > 2)
548 		printf("Error: find duplicate(%d) dtbs\n", num);
549 
550 	return target_file ? : get_file_info(DEFAULT_DTB_FILE);
551 }
552 
553 int rockchip_read_resource_dtb(void *fdt_addr, char **hash, int *hash_size)
554 {
555 	struct resource_file *file = NULL;
556 	int ret;
557 
558 #ifdef CONFIG_ROCKCHIP_HWID_DTB
559 	file = resource_read_hwid_dtb();
560 #endif
561 	/* If no dtb matches hardware id(GPIO/ADC), use the default */
562 	if (!file)
563 		file = get_default_dtb();
564 
565 	if (!file)
566 		return -ENODEV;
567 
568 	ret = rockchip_read_resource_file(fdt_addr, file->name, 0, 0);
569 	if (ret < 0)
570 		return ret;
571 
572 	if (fdt_check_header(fdt_addr))
573 		return -EBADF;
574 
575 	*hash = file->hash;
576 	*hash_size = file->hash_size;
577 	printf("DTB: %s\n", file->name);
578 
579 	return 0;
580 }
581 
582 int resource_populate_dtb(void *img, void *fdt)
583 {
584 	struct blk_desc *dev_desc;
585 	int format;
586 	int ret;
587 
588 	format = (genimg_get_format(img));
589 #ifdef CONFIG_ANDROID_BOOT_IMAGE
590 	if (format == IMAGE_FORMAT_ANDROID) {
591 		struct andr_img_hdr *hdr = img;
592 		ulong offset;
593 
594 		dev_desc = rockchip_get_bootdev();
595 		if (!dev_desc)
596 			return -ENODEV;
597 
598 		offset = hdr->page_size + ALIGN(hdr->kernel_size, hdr->page_size) +
599 			ALIGN(hdr->ramdisk_size, hdr->page_size);
600 		ret = resource_create_ram_list(dev_desc, (void *)hdr + offset);
601 		if (ret)
602 			return ret;
603 
604 		return rockchip_read_dtb_file((void *)fdt);
605 	}
606 #endif
607 #if IMAGE_ENABLE_FIT
608 	if (format == IMAGE_FORMAT_FIT) {
609 		const void *data;
610 		size_t size;
611 		int noffset;
612 
613 		noffset = fdt_path_offset(img, "/images/resource");
614 		if (noffset < 0)
615 			return noffset;
616 
617 		ret = fit_image_get_data(img, noffset, &data, &size);
618 		if (ret < 0)
619 			return ret;
620 
621 		dev_desc = rockchip_get_bootdev();
622 		if (!dev_desc)
623 			return -ENODEV;
624 
625 		ret = resource_create_ram_list(dev_desc, (void *)data);
626 		if (ret) {
627 			printf("resource_create_ram_list fail, ret=%d\n", ret);
628 			return ret;
629 		}
630 
631 		return rockchip_read_dtb_file((void *)fdt);
632 	}
633 #endif
634 
635 	return -EINVAL;
636 }
637 
638 int resource_traverse_init_list(void)
639 {
640 	if (!resource_is_empty())
641 		return 0;
642 
643 #ifdef CONFIG_ROCKCHIP_FIT_IMAGE
644 	if (!fit_image_init_resource())
645 		return 0;
646 #endif
647 #ifdef CONFIG_ROCKCHIP_UIMAGE
648 	if (!uimage_init_resource())
649 		return 0;
650 #endif
651 	/* Android image is default supported within resource core */
652 
653 	return 0;
654 }
655 
656 static int do_dump_resource(cmd_tbl_t *cmdtp, int flag,
657 			    int argc, char *const argv[])
658 {
659 	struct resource_file *file;
660 	struct list_head *node;
661 
662 	printf("Resources:\n");
663 	list_for_each(node, &entrys_head) {
664 		file = list_entry(node, struct resource_file, link);
665 		printf("	%s: 0x%08x(sector), 0x%08x(bytes)\n",
666 		       file->name, file->rsce_base + file->f_offset, file->f_size);
667 	}
668 
669 #ifdef CONFIG_ROCKCHIP_HWID_DTB
670 	printf("DTBs:\n");
671 	list_for_each(node, &entrys_dtbs_head) {
672 		file = list_entry(node, struct resource_file, dtbs);
673 		printf("	%s: 0x%08x(sector),0x%08x(bytes)\n",
674 		       file->name, file->rsce_base + file->f_offset, file->f_size);
675 	}
676 #endif
677 	return 0;
678 }
679 
680 U_BOOT_CMD(
681 	dump_resource, 1, 1, do_dump_resource,
682 	"dump resource list",
683 	""
684 );
685 
686