xref: /rk3399_rockchip-uboot/arch/arm/mach-rockchip/resource_img.c (revision e12b5efd268e1005d4dd24711b7aeefc0edf34bb)
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 	INIT_LIST_HEAD(&file->dtbs);
158 	list_add_tail(&file->link, &entrys_head);
159 	if (strstr(file->name, DTB_SUFFIX) && board_resource_dtb_accepted(file->name))
160 		list_add_tail(&file->dtbs, &entrys_dtbs_head);
161 	debug("ENTRY: addr: %p, name: %18s, base: 0x%08x, offset: 0x%08x, size: 0x%08x\n",
162 	      entry, file->name, file->rsce_base, file->f_offset, file->f_size);
163 
164 	return 0;
165 }
166 
167 int resource_replace_entry(const char *f_name, uint32_t base,
168 			   uint32_t f_offset, uint32_t f_size)
169 {
170 	struct resource_entry *entry;
171 	struct resource_file *file;
172 	struct list_head *node;
173 
174 	if (!f_name || !f_size)
175 		return -EINVAL;
176 
177 	entry = calloc(1, sizeof(*entry));
178 	if (!entry)
179 		return -ENOMEM;
180 
181 	strcpy(entry->tag, ENTRY_TAG);
182 	strcpy(entry->name, f_name);
183 	entry->f_offset = f_offset;
184 	entry->f_size = f_size;
185 	entry->hash_size = 0;
186 
187 	/* Delete exist entry, then add this new */
188 	list_for_each(node, &entrys_head) {
189 		file = list_entry(node, struct resource_file, link);
190 		if (!strcmp(file->name, entry->name)) {
191 			list_del(&file->link);
192 			list_del(&file->dtbs);
193 			free(file);
194 			break;
195 		}
196 	}
197 
198 	add_file_to_list(entry, base, false);
199 	free(entry);
200 
201 	return 0;
202 }
203 
204 int resource_create_ram_list(struct blk_desc *dev_desc, void *rsce_hdr)
205 {
206 	struct resource_img_hdr *hdr = rsce_hdr;
207 	struct resource_entry *entry;
208 	int e_num, size;
209 	void *data;
210 	int ret = 0;
211 
212 	if (resource_image_check_header(hdr)) {
213 		ret = -EINVAL;
214 		goto out;
215 	}
216 
217 	list_del_init(&entrys_head);
218 	list_del_init(&entrys_dtbs_head);
219 	data = (void *)((ulong)hdr + hdr->c_offset * dev_desc->blksz);
220 	for (e_num = 0; e_num < hdr->e_nums; e_num++) {
221 		size = e_num * hdr->e_blks * dev_desc->blksz;
222 		entry = (struct resource_entry *)(data + size);
223 		add_file_to_list(entry, (ulong)hdr, true);
224 	}
225 out:
226 	resource_read_logo_bmps();
227 
228 	return ret;
229 }
230 
231 static int resource_create_list(struct blk_desc *dev_desc, int rsce_base)
232 {
233 	struct resource_img_hdr *hdr;
234 	struct resource_entry *entry;
235 	int blknum, e_num;
236 	void *data = NULL;
237 	int ret = 0;
238 	int size;
239 
240 	hdr = memalign(ARCH_DMA_MINALIGN, dev_desc->blksz);
241 	if (!hdr)
242 		return -ENOMEM;
243 
244 	if (blk_dread(dev_desc, rsce_base, 1, hdr) != 1) {
245 		printf("Failed to read resource hdr\n");
246 		ret = -EIO;
247 		goto err;
248 	}
249 
250 	if (resource_image_check_header(hdr)) {
251 		if (fdt_check_header(hdr)) {
252 			printf("No valid resource or dtb file\n");
253 			ret = -EINVAL;
254 			goto err;
255 		} else {
256 			free(hdr);
257 			return resource_replace_entry(DEFAULT_DTB_FILE, rsce_base,
258 						      0, fdt_totalsize(hdr));
259 		}
260 	}
261 
262 	blknum = hdr->e_blks * hdr->e_nums;
263 	data = memalign(ARCH_DMA_MINALIGN, blknum * dev_desc->blksz);
264 	if (!data) {
265 		ret = -ENOMEM;
266 		goto err;
267 	}
268 
269 	if (blk_dread(dev_desc, rsce_base + hdr->c_offset,
270 		      blknum, data) != blknum) {
271 		printf("Failed to read resource entries\n");
272 		ret = -EIO;
273 		goto err;
274 	}
275 
276 	/*
277 	 * Add all file into resource file list, and load what we want from
278 	 * storage when we really need it.
279 	 */
280 	for (e_num = 0; e_num < hdr->e_nums; e_num++) {
281 		size = e_num * hdr->e_blks * dev_desc->blksz;
282 		entry = (struct resource_entry *)(data + size);
283 		add_file_to_list(entry, rsce_base, false);
284 	}
285 
286 err:
287 	if (data)
288 		free(data);
289 	if (hdr)
290 		free(hdr);
291 
292 	resource_read_logo_bmps();
293 
294 	return ret;
295 }
296 
297 static int read_dtb_from_android(struct blk_desc *dev_desc,
298 				 struct andr_img_hdr *hdr,
299 				 ulong rsce_base)
300 {
301 	ulong dtb_offset = 0;
302 	ulong dtb_size = 0;
303 
304 	if (!hdr || hdr->header_version <= 1) {
305 		return 0;
306 	} else if (hdr->header_version == 2) {
307 		dtb_offset += hdr->page_size;
308 		dtb_offset += ALIGN(hdr->kernel_size, hdr->page_size);
309 		dtb_offset += ALIGN(hdr->ramdisk_size, hdr->page_size);
310 		dtb_offset += ALIGN(hdr->recovery_dtbo_size, hdr->page_size) +
311 			      ALIGN(hdr->second_size, hdr->page_size);
312 		dtb_size = hdr->dtb_size;
313 	} else if (hdr->header_version >= 3) {
314 		ulong vendor_boot_hdr_size = (hdr->header_version == 3) ?
315 			VENDOR_BOOT_HDRv3_SIZE : VENDOR_BOOT_HDRv4_SIZE;
316 
317 		dtb_offset += ALIGN(vendor_boot_hdr_size,
318 				    hdr->vendor_page_size) +
319 			      ALIGN(hdr->vendor_ramdisk_size,
320 				    hdr->vendor_page_size);
321 		dtb_size = hdr->dtb_size;
322 	}
323 
324 	if (!dtb_size)
325 		return 0;
326 
327 	/*
328 	 * boot_img_hdr_v234 feature.
329 	 *
330 	 * If dtb position is present, replace the old with new one if
331 	 * we don't need to verify DTB hash from resource.img file entry.
332 	 */
333 	dtb_offset = DIV_ROUND_UP(dtb_offset, dev_desc->blksz);
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_traverse_init_list(void)
579 {
580 	if (!resource_is_empty())
581 		return 0;
582 
583 #ifdef CONFIG_ROCKCHIP_FIT_IMAGE
584 	if (!fit_image_init_resource())
585 		return 0;
586 #endif
587 #ifdef CONFIG_ROCKCHIP_UIMAGE
588 	if (!uimage_init_resource())
589 		return 0;
590 #endif
591 	/* Android image is default supported within resource core */
592 
593 	return 0;
594 }
595 
596 static int do_dump_resource(cmd_tbl_t *cmdtp, int flag,
597 			    int argc, char *const argv[])
598 {
599 	struct resource_file *file;
600 	struct list_head *node;
601 
602 	printf("Resources:\n");
603 	list_for_each(node, &entrys_head) {
604 		file = list_entry(node, struct resource_file, link);
605 		printf("	%s: 0x%08x(sector), 0x%08x(bytes)\n",
606 		       file->name, file->rsce_base + file->f_offset, file->f_size);
607 	}
608 
609 #ifdef CONFIG_ROCKCHIP_HWID_DTB
610 	printf("DTBs:\n");
611 	list_for_each(node, &entrys_dtbs_head) {
612 		file = list_entry(node, struct resource_file, dtbs);
613 		printf("	%s: 0x%08x(sector),0x%08x(bytes)\n",
614 		       file->name, file->rsce_base + file->f_offset, file->f_size);
615 	}
616 #endif
617 	return 0;
618 }
619 
620 U_BOOT_CMD(
621 	dump_resource, 1, 1, do_dump_resource,
622 	"dump resource list",
623 	""
624 );
625 
626