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