xref: /rk3399_rockchip-uboot/arch/arm/mach-rockchip/resource_img.c (revision ff06e2a5e708b41585eb4e144fdbd610307fa815)
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 			free(file);
192 			break;
193 		}
194 	}
195 
196 	add_file_to_list(entry, base, false);
197 	free(entry);
198 
199 	return 0;
200 }
201 
202 int resource_create_ram_list(struct blk_desc *dev_desc, void *rsce_hdr)
203 {
204 	struct resource_img_hdr *hdr = rsce_hdr;
205 	struct resource_entry *entry;
206 	int e_num, size;
207 	void *data;
208 	int ret = 0;
209 
210 	if (resource_image_check_header(hdr)) {
211 		ret = -EINVAL;
212 		goto out;
213 	}
214 
215 	list_del_init(&entrys_head);
216 	list_del_init(&entrys_dtbs_head);
217 	data = (void *)((ulong)hdr + hdr->c_offset * dev_desc->blksz);
218 	for (e_num = 0; e_num < hdr->e_nums; e_num++) {
219 		size = e_num * hdr->e_blks * dev_desc->blksz;
220 		entry = (struct resource_entry *)(data + size);
221 		add_file_to_list(entry, (ulong)hdr, true);
222 	}
223 out:
224 	resource_read_logo_bmps();
225 
226 	return ret;
227 }
228 
229 static int resource_create_list(struct blk_desc *dev_desc, int rsce_base)
230 {
231 	struct resource_img_hdr *hdr;
232 	struct resource_entry *entry;
233 	int blknum, e_num;
234 	void *data = NULL;
235 	int ret = 0;
236 	int size;
237 
238 	hdr = memalign(ARCH_DMA_MINALIGN, dev_desc->blksz);
239 	if (!hdr)
240 		return -ENOMEM;
241 
242 	if (blk_dread(dev_desc, rsce_base, 1, hdr) != 1) {
243 		printf("Failed to read resource hdr\n");
244 		ret = -EIO;
245 		goto err;
246 	}
247 
248 	if (resource_image_check_header(hdr)) {
249 		if (fdt_check_header(hdr)) {
250 			printf("No valid resource or dtb file\n");
251 			ret = -EINVAL;
252 			goto err;
253 		} else {
254 			free(hdr);
255 			return resource_replace_entry(DEFAULT_DTB_FILE, rsce_base,
256 						      0, fdt_totalsize(hdr));
257 		}
258 	}
259 
260 	blknum = hdr->e_blks * hdr->e_nums;
261 	data = memalign(ARCH_DMA_MINALIGN, blknum * dev_desc->blksz);
262 	if (!data) {
263 		ret = -ENOMEM;
264 		goto err;
265 	}
266 
267 	if (blk_dread(dev_desc, rsce_base + hdr->c_offset,
268 		      blknum, data) != blknum) {
269 		printf("Failed to read resource entries\n");
270 		ret = -EIO;
271 		goto err;
272 	}
273 
274 	/*
275 	 * Add all file into resource file list, and load what we want from
276 	 * storage when we really need it.
277 	 */
278 	for (e_num = 0; e_num < hdr->e_nums; e_num++) {
279 		size = e_num * hdr->e_blks * dev_desc->blksz;
280 		entry = (struct resource_entry *)(data + size);
281 		add_file_to_list(entry, rsce_base, false);
282 	}
283 
284 err:
285 	if (data)
286 		free(data);
287 	if (hdr)
288 		free(hdr);
289 
290 	resource_read_logo_bmps();
291 
292 	return ret;
293 }
294 
295 static int read_dtb_from_android(struct blk_desc *dev_desc,
296 				 struct andr_img_hdr *hdr,
297 				 ulong rsce_base)
298 {
299 	ulong dtb_offset = 0;
300 	ulong dtb_size = 0;
301 
302 	if (!hdr || hdr->header_version <= 1) {
303 		return 0;
304 	} else if (hdr->header_version == 2) {
305 		dtb_offset += hdr->page_size;
306 		dtb_offset += ALIGN(hdr->kernel_size, hdr->page_size);
307 		dtb_offset += ALIGN(hdr->ramdisk_size, hdr->page_size);
308 		dtb_offset += ALIGN(hdr->recovery_dtbo_size, hdr->page_size) +
309 			      ALIGN(hdr->second_size, hdr->page_size);
310 		dtb_size = hdr->dtb_size;
311 	} else if (hdr->header_version >= 3) {
312 		ulong vendor_boot_hdr_size = (hdr->header_version == 3) ?
313 			VENDOR_BOOT_HDRv3_SIZE : VENDOR_BOOT_HDRv4_SIZE;
314 
315 		dtb_offset += ALIGN(vendor_boot_hdr_size,
316 				    hdr->vendor_page_size) +
317 			      ALIGN(hdr->vendor_ramdisk_size,
318 				    hdr->vendor_page_size);
319 		dtb_size = hdr->dtb_size;
320 	}
321 
322 	if (!dtb_size)
323 		return 0;
324 
325 	/*
326 	 * boot_img_hdr_v234 feature.
327 	 *
328 	 * If dtb position is present, replace the old with new one if
329 	 * we don't need to verify DTB hash from resource.img file entry.
330 	 */
331 	dtb_offset = DIV_ROUND_UP(dtb_offset, dev_desc->blksz);
332 #ifndef CONFIG_ROCKCHIP_DTB_VERIFY
333 	if (replace_resource_entry(DEFAULT_DTB_FILE, rsce_base, dtb_offset, dtb_size))
334 		printf("Failed to load dtb from v2 dtb position\n");
335 	else
336 #endif
337 		env_update("bootargs", "androidboot.dtb_idx=0");
338 
339 	return 0;
340 }
341 
342 static int get_resource_base_sector(struct blk_desc *dev_desc,
343 				    struct andr_img_hdr **ret_hdr)
344 {
345 	disk_partition_t part;
346 	int rsce_base = 0;
347 #ifdef CONFIG_ANDROID_BOOT_IMAGE
348 	struct andr_img_hdr *hdr;
349 	u32 os_ver = 0, os_lvl;
350 	const char *part_boot = PART_BOOT;
351 
352 	/*
353 	 * Anyway, we must read android hdr firstly from boot/recovery partition
354 	 * to get the 'os_version' for android_bcb_msg_sector_offset(), in order
355 	 * to confirm BCB message offset of *MISC* partition.
356 	 */
357 #ifdef CONFIG_ANDROID_AB
358 	part_boot = ab_can_find_recovery_part() ? PART_RECOVERY : PART_BOOT;
359 #endif
360 
361 	if (part_get_info_by_name(dev_desc, part_boot, &part) < 0)
362 		goto resource_part;
363 
364 	hdr = populate_andr_img_hdr(dev_desc, &part);
365 	if (hdr) {
366 		os_ver = hdr->os_version >> 11;
367 		os_lvl = hdr->os_version & ((1U << 11) - 1);
368 		if (os_ver)
369 			gd->bd->bi_andr_version = hdr->os_version;
370 	}
371 
372 #ifndef CONFIG_ANDROID_AB
373 	/* Get boot mode from misc and read if recovery mode */
374 	if (rockchip_get_boot_mode() == BOOT_MODE_RECOVERY) {
375 		if (hdr)
376 			free(hdr);
377 
378 		if (part_get_info_by_name(dev_desc, PART_RECOVERY, &part) < 0)
379 			goto resource_part;
380 
381 		hdr = populate_andr_img_hdr(dev_desc, &part);
382 		if (!hdr)
383 			goto resource_part;
384 	}
385 #endif
386 	/* If Android v012, getting resource from second position ! */
387 	if (hdr) {
388 		if (os_ver)
389 			printf("Android %u.%u, Build %u.%u, v%d\n",
390 			       (os_ver >> 14) & 0x7F, (os_ver >> 7) & 0x7F,
391 			       (os_lvl >> 4) + 2000, os_lvl & 0x0F,
392 			       hdr->header_version);
393 		*ret_hdr = hdr;
394 		if (hdr->header_version < 3) {
395 			rsce_base = part.start * dev_desc->blksz;
396 			rsce_base += hdr->page_size;
397 			rsce_base += ALIGN(hdr->kernel_size, hdr->page_size);
398 			rsce_base += ALIGN(hdr->ramdisk_size, hdr->page_size);
399 			rsce_base = DIV_ROUND_UP(rsce_base, dev_desc->blksz);
400 			goto finish;
401 		}
402 	}
403 resource_part:
404 #endif
405 	/* resource partition */
406 	if (part_get_info_by_name(dev_desc, PART_RESOURCE, &part) < 0) {
407 		printf("No resource partition\n");
408 		return -ENODEV;
409 	} else {
410 		rsce_base = part.start;
411 	}
412 #ifdef CONFIG_ANDROID_BOOT_IMAGE
413 finish:
414 #endif
415 	printf("Found DTB in %s part\n", part.name);
416 
417 	return rsce_base;
418 }
419 
420 /*
421  * There are: logo/battery pictures and dtb file in the resource image by default.
422  *
423  * This function does:
424  *
425  * 1. Get resource image base sector from: boot/recovery(AOSP) > resource(RK)
426  * 2. Create resource files list(addition: add logo bmps)
427  * 3. Add dtb from android v2 dtb pos, override the old one from resource file
428  */
429 int resource_init_list(void)
430 {
431 	struct andr_img_hdr *hdr = NULL;
432 	struct blk_desc *dev_desc;
433 	int rsce_base;
434 
435 	dev_desc = rockchip_get_bootdev();
436 	if (!dev_desc) {
437 		printf("No dev_desc!\n");
438 		return -ENODEV;
439 	}
440 
441 	rsce_base = get_resource_base_sector(dev_desc, &hdr);
442 	if (rsce_base > 0) {
443 		if (resource_create_list(dev_desc, rsce_base))
444 			printf("Failed to create resource list\n");
445 	}
446 
447 	/* override the resource dtb with android dtb if need */
448 	return read_dtb_from_android(dev_desc, hdr, rsce_base);
449 }
450 
451 int resource_is_empty(void)
452 {
453 	return list_empty(&entrys_head);
454 }
455 
456 static struct resource_file *get_file_info(const char *name)
457 {
458 	struct resource_file *file;
459 	struct list_head *node;
460 
461 	if (list_empty(&entrys_head)) {
462 		if (resource_init_list())
463 			return NULL;
464 	}
465 
466 	list_for_each(node, &entrys_head) {
467 		file = list_entry(node, struct resource_file, link);
468 		if (!strcmp(file->name, name))
469 			return file;
470 	}
471 
472 	return NULL;
473 }
474 
475 /*
476  * read file from resource partition
477  * @buf: destination buf to store file data;
478  * @name: file name
479  * @offset: blocks offset in the file, 1 block = 512 bytes
480  * @len: the size(by bytes) of file to read.
481  */
482 int rockchip_read_resource_file(void *buf, const char *name, int offset, int len)
483 {
484 	struct resource_file *file;
485 	struct blk_desc *dev_desc;
486 	int ret = 0;
487 	int blks;
488 	ulong src;
489 
490 	file = get_file_info(name);
491 	if (!file) {
492 		printf("No file: %s\n", name);
493 		return -ENOENT;
494 	}
495 
496 	dev_desc = rockchip_get_bootdev();
497 	if (!dev_desc) {
498 		printf("No dev_desc!\n");
499 		return -ENODEV;
500 	}
501 
502 	if (len <= 0 || len > file->f_size)
503 		len = file->f_size;
504 
505 	if (file->ram) {
506 		src = file->rsce_base +
507 			(file->f_offset + offset) * dev_desc->blksz;
508 		memcpy(buf, (char *)src, len);
509 		ret = len;
510 	} else {
511 		blks = DIV_ROUND_UP(len, dev_desc->blksz);
512 		ret = blk_dread(dev_desc,
513 				file->rsce_base + file->f_offset + offset,
514 				blks, buf);
515 		ret = (ret != blks) ? -EIO : len;
516 	}
517 
518 	return ret;
519 }
520 
521 static struct resource_file *get_default_dtb(void)
522 {
523 	struct resource_file *target_file = NULL;
524 	struct resource_file *file;
525 	struct list_head *node;
526 	int num = 0;
527 
528 	if (list_empty(&entrys_head)) {
529 		if (resource_init_list())
530 			return NULL;
531 	}
532 
533 	list_for_each(node, &entrys_dtbs_head) {
534 		num++;
535 		file = list_entry(node, struct resource_file, dtbs);
536 		if (strcmp(file->name, DEFAULT_DTB_FILE))
537 			target_file = file;
538 	}
539 
540 	/*
541 	 * two possible case:
542 	 *	case 1. rk-kernel.dtb only
543 	 *	case 2. targe_file(s) + rk-kernel.dtb(maybe they are the same),
544 	 *		use (last)target_file as result one.
545 	 */
546 	if (num > 2)
547 		printf("Error: find duplicate(%d) dtbs\n", num);
548 
549 	return target_file ? : get_file_info(DEFAULT_DTB_FILE);
550 }
551 
552 int rockchip_read_resource_dtb(void *fdt_addr, char **hash, int *hash_size)
553 {
554 	struct resource_file *file = NULL;
555 	int ret;
556 
557 #ifdef CONFIG_ROCKCHIP_HWID_DTB
558 	file = resource_read_hwid_dtb();
559 #endif
560 	/* If no dtb matches hardware id(GPIO/ADC), use the default */
561 	if (!file)
562 		file = get_default_dtb();
563 
564 	if (!file)
565 		return -ENODEV;
566 
567 	ret = rockchip_read_resource_file(fdt_addr, file->name, 0, 0);
568 	if (ret < 0)
569 		return ret;
570 
571 	if (fdt_check_header(fdt_addr))
572 		return -EBADF;
573 
574 	*hash = file->hash;
575 	*hash_size = file->hash_size;
576 	printf("DTB: %s\n", file->name);
577 
578 	return 0;
579 }
580 
581 int resource_populate_dtb(void *img, void *fdt)
582 {
583 	struct blk_desc *dev_desc;
584 	int format;
585 	int ret;
586 
587 	format = (genimg_get_format(img));
588 #ifdef CONFIG_ANDROID_BOOT_IMAGE
589 	if (format == IMAGE_FORMAT_ANDROID) {
590 		struct andr_img_hdr *hdr = img;
591 		ulong offset;
592 
593 		dev_desc = rockchip_get_bootdev();
594 		if (!dev_desc)
595 			return -ENODEV;
596 
597 		offset = hdr->page_size + ALIGN(hdr->kernel_size, hdr->page_size) +
598 			ALIGN(hdr->ramdisk_size, hdr->page_size);
599 		ret = resource_create_ram_list(dev_desc, (void *)hdr + offset);
600 		if (ret)
601 			return ret;
602 
603 		return rockchip_read_dtb_file((void *)fdt);
604 	}
605 #endif
606 #if IMAGE_ENABLE_FIT
607 	if (format == IMAGE_FORMAT_FIT) {
608 		const void *data;
609 		size_t size;
610 		int noffset;
611 
612 		noffset = fdt_path_offset(img, "/images/resource");
613 		if (noffset < 0)
614 			return noffset;
615 
616 		ret = fit_image_get_data(img, noffset, &data, &size);
617 		if (ret < 0)
618 			return ret;
619 
620 		dev_desc = rockchip_get_bootdev();
621 		if (!dev_desc)
622 			return -ENODEV;
623 
624 		ret = resource_create_ram_list(dev_desc, (void *)data);
625 		if (ret) {
626 			printf("resource_create_ram_list fail, ret=%d\n", ret);
627 			return ret;
628 		}
629 
630 		return rockchip_read_dtb_file((void *)fdt);
631 	}
632 #endif
633 
634 	return -EINVAL;
635 }
636 
637 int resource_traverse_init_list(void)
638 {
639 	if (!resource_is_empty())
640 		return 0;
641 
642 #ifdef CONFIG_ROCKCHIP_FIT_IMAGE
643 	if (!fit_image_init_resource())
644 		return 0;
645 #endif
646 #ifdef CONFIG_ROCKCHIP_UIMAGE
647 	if (!uimage_init_resource())
648 		return 0;
649 #endif
650 	/* Android image is default supported within resource core */
651 
652 	return 0;
653 }
654 
655 static int do_dump_resource(cmd_tbl_t *cmdtp, int flag,
656 			    int argc, char *const argv[])
657 {
658 	struct resource_file *file;
659 	struct list_head *node;
660 
661 	printf("Resources:\n");
662 	list_for_each(node, &entrys_head) {
663 		file = list_entry(node, struct resource_file, link);
664 		printf("	%s: 0x%08x(sector), 0x%08x(bytes)\n",
665 		       file->name, file->rsce_base + file->f_offset, file->f_size);
666 	}
667 
668 #ifdef CONFIG_ROCKCHIP_HWID_DTB
669 	printf("DTBs:\n");
670 	list_for_each(node, &entrys_dtbs_head) {
671 		file = list_entry(node, struct resource_file, dtbs);
672 		printf("	%s: 0x%08x(sector),0x%08x(bytes)\n",
673 		       file->name, file->rsce_base + file->f_offset, file->f_size);
674 	}
675 #endif
676 	return 0;
677 }
678 
679 U_BOOT_CMD(
680 	dump_resource, 1, 1, do_dump_resource,
681 	"dump resource list",
682 	""
683 );
684 
685