xref: /rk3399_rockchip-uboot/arch/arm/mach-rockchip/resource_img.c (revision 06a213412b027f93b07f8d3fea776fb12b505ea1)
1 /*
2  * (C) Copyright 2017 Rockchip Electronics Co., Ltd
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6 #include <common.h>
7 #include <boot_rkimg.h>
8 #include <bmp_layout.h>
9 #include <malloc.h>
10 #include <asm/unaligned.h>
11 #include <linux/libfdt.h>
12 #include <linux/list.h>
13 #include <asm/arch/resource_img.h>
14 #include <asm/arch/rk_hwid.h>
15 #include <asm/arch/uimage.h>
16 #include <asm/arch/fit.h>
17 
18 DECLARE_GLOBAL_DATA_PTR;
19 
20 #define PART_RESOURCE			"resource"
21 #define RESOURCE_MAGIC			"RSCE"
22 #define RESOURCE_MAGIC_SIZE		4
23 #define ENTRY_TAG			"ENTR"
24 #define ENTRY_TAG_SIZE			4
25 #define MAX_FILE_NAME_LEN		220
26 #define MAX_HASH_LEN			32
27 #define DEFAULT_DTB_FILE		"rk-kernel.dtb"
28 
29 /*
30  *         resource image structure
31  * ----------------------------------------------
32  * |                                            |
33  * |    header  (1 block)                       |
34  * |                                            |
35  * ---------------------------------------------|
36  * |                      |                     |
37  * |    entry0  (1 block) |                     |
38  * |                      |                     |
39  * ------------------------                     |
40  * |                      |                     |
41  * |    entry1  (1 block) | contents (n blocks) |
42  * |                      |                     |
43  * ------------------------                     |
44  * |    ......            |                     |
45  * ------------------------                     |
46  * |                      |                     |
47  * |    entryn  (1 block) |                     |
48  * |                      |                     |
49  * ----------------------------------------------
50  * |                                            |
51  * |    file0  (x blocks)                       |
52  * |                                            |
53  * ----------------------------------------------
54  * |                                            |
55  * |    file1  (y blocks)                       |
56  * |                                            |
57  * ----------------------------------------------
58  * |                   ......                   |
59  * |---------------------------------------------
60  * |                                            |
61  * |    filen  (z blocks)                       |
62  * |                                            |
63  * ----------------------------------------------
64  */
65 
66 /**
67  * struct resource_img_hdr
68  *
69  * @magic: should be "RSCE"
70  * @version: resource image version, current is 0
71  * @c_version: content version, current is 0
72  * @blks: the size of the header ( 1 block = 512 bytes)
73  * @c_offset: contents offset(by block) in the image
74  * @e_blks: the size(by block) of the entry in the contents
75  * @e_num: numbers of the entrys.
76  */
77 struct resource_img_hdr {
78 	char		magic[4];
79 	uint16_t	version;
80 	uint16_t	c_version;
81 	uint8_t		blks;
82 	uint8_t		c_offset;
83 	uint8_t		e_blks;
84 	uint32_t	e_nums;
85 };
86 
87 struct resource_entry {
88 	char		tag[4];
89 	char		name[MAX_FILE_NAME_LEN];
90 	char		hash[MAX_HASH_LEN];
91 	uint32_t	hash_size;
92 	uint32_t	blk_offset;
93 	uint32_t	size;		/* in byte */
94 };
95 
96 LIST_HEAD(entry_head);
97 
resource_check_header(struct resource_img_hdr * hdr)98 static int resource_check_header(struct resource_img_hdr *hdr)
99 {
100 	return memcmp(RESOURCE_MAGIC, hdr->magic, RESOURCE_MAGIC_SIZE);
101 }
102 
resource_dump(struct resource_file * f)103 static void resource_dump(struct resource_file *f)
104 {
105 	printf("%s\n", f->name);
106 	printf("  blk_start:  0x%08lx\n", f->blk_start);
107 	printf("  blk_offset: 0x%08lx\n", f->blk_offset);
108 	printf("  size:       0x%08x\n", f->size);
109 	printf("  in_ram:     %d\n", f->in_ram);
110 	printf("  hash_size:  %d\n\n", f->hash_size);
111 }
112 
resource_add_file(const char * name,u32 size,ulong blk_start,u32 blk_offset,char * hash,u32 hash_size,bool in_ram)113 static int resource_add_file(const char *name, u32 size,
114 			     ulong blk_start,  u32 blk_offset,
115 			     char *hash, u32 hash_size,
116 			     bool in_ram)
117 {
118 	struct resource_file *f;
119 	struct list_head *node;
120 	bool _new = true;
121 
122 	/* old one ? */
123 	list_for_each(node, &entry_head) {
124 		f = list_entry(node, struct resource_file, link);
125 		if (!strcmp(f->name, name)) {
126 			_new = false;
127 			break;
128 		}
129 	}
130 
131 	if (_new) {
132 		f = calloc(1, sizeof(*f));
133 		if (!f)
134 			return -ENOMEM;
135 
136 		list_add_tail(&f->link, &entry_head);
137 	}
138 
139 	strcpy(f->name, name);
140 	f->size       = size;
141 	f->in_ram     = in_ram;
142 	f->blk_start  = blk_start;
143 	f->blk_offset = blk_offset;
144 	f->hash_size  = hash_size;
145 	memcpy(f->hash, hash, hash_size);
146 #ifdef DEBUG
147 	resource_dump(f);
148 #endif
149 	return 0;
150 }
151 
resource_destroy(void)152 void resource_destroy(void)
153 {
154 	INIT_LIST_HEAD(&entry_head);
155 }
156 
157 #ifdef CONFIG_ANDROID_BOOT_IMAGE
158 /*
159  * Add logo.bmp and logo_kernel.bmp from "logo" parititon
160  *
161  * Provide a "logo" partition for user to store logo.bmp and
162  * logo_kernel.bmp, so that the user can update them from
163  * kernel or user-space dynamically.
164  *
165  * "logo" partition layout, do not change order:
166  *
167  *   |----------------------| 0x00
168  *   | raw logo.bmp	    |
169  *   |----------------------| -> 512-byte aligned
170  *   | raw logo_kernel.bmp  |
171  *   |----------------------|
172  *
173  * N: the sector count of logo.bmp
174  *
175  * How to generate:
176  * 	cat logo.bmp > logo.img && truncate -s %512 logo.img && cat logo_kernel.bmp >> logo.img
177  */
resource_setup_logo_bmp(struct blk_desc * desc)178 static int resource_setup_logo_bmp(struct blk_desc *desc)
179 {
180 	struct bmp_header *header;
181 	const char *name[] = { "logo.bmp", "logo_kernel.bmp" };
182 	disk_partition_t part;
183 	u32 blk_offset = 0;
184 	u32 filesz;
185 	int ret, i;
186 
187 	if (part_get_info_by_name(desc, PART_LOGO, &part) < 0)
188 		return 0;
189 
190 	header = memalign(ARCH_DMA_MINALIGN, desc->blksz);
191 	if (!header)
192 		return -ENOMEM;
193 
194 	for (i = 0; i < ARRAY_SIZE(name); i++) {
195 		if (blk_dread(desc, part.start + blk_offset, 1, header) != 1) {
196 			ret = -EIO;
197 			break;
198 		}
199 
200 		if (header->signature[0] != 'B' || header->signature[1] != 'M') {
201 			ret = -EINVAL;
202 			break;
203 		}
204 
205 		filesz = get_unaligned_le32(&header->file_size);
206 		ret = resource_add_file(name[i], filesz, part.start, blk_offset,
207 					NULL, 0, false);
208 		if (ret)
209 			break;
210 
211 		/* move to next file */
212 		blk_offset += DIV_ROUND_UP(filesz, desc->blksz);
213 
214 		printf("LOGO: %s\n", name[i]);
215 
216 	}
217 
218 	free(header);
219 
220 	return ret;
221 }
222 #endif
223 
resource_setup_list(struct blk_desc * desc,ulong blk_start,void * resc_hdr,bool in_ram)224 static int resource_setup_list(struct blk_desc *desc, ulong blk_start,
225 			       void *resc_hdr, bool in_ram)
226 {
227 	struct resource_img_hdr *hdr = resc_hdr;
228 	struct resource_entry *et;
229 	u32 i, stride;
230 	void *pos;
231 
232 	pos = (void *)hdr + hdr->c_offset * desc->blksz;
233 	stride = hdr->e_blks * desc->blksz;
234 
235 	for (i = 0; i < hdr->e_nums; i++) {
236 		et = pos + (i * stride);
237 		if (memcmp(et->tag, ENTRY_TAG, ENTRY_TAG_SIZE))
238 			continue;
239 
240 		resource_add_file(et->name, et->size,
241 				  blk_start, et->blk_offset,
242 				  et->hash, et->hash_size, in_ram);
243 	}
244 #ifdef CONFIG_ANDROID_BOOT_IMAGE
245 	resource_setup_logo_bmp(desc);
246 #endif
247 	return 0;
248 }
249 
resource_setup_ram_list(struct blk_desc * desc,void * hdr)250 int resource_setup_ram_list(struct blk_desc *desc, void *hdr)
251 {
252 	if (!desc)
253 		return -ENODEV;
254 
255 	if (resource_check_header(hdr)) {
256 		printf("RESC: invalid\n");
257 		return -EINVAL;
258 	}
259 
260 	/* @blk_start: set as 'hdr' point addr, to be used in byte */
261 	return resource_setup_list(desc, (ulong)hdr, hdr, true);
262 }
263 
264 #ifdef CONFIG_ANDROID_BOOT_IMAGE
resource_setup_blk_list(struct blk_desc * desc,ulong blk_start)265 static int resource_setup_blk_list(struct blk_desc *desc, ulong blk_start)
266 {
267 	struct resource_img_hdr *hdr;
268 	int blk_cnt;
269 	int ret = 0;
270 	void *buf;
271 
272 	hdr = memalign(ARCH_DMA_MINALIGN, desc->blksz);
273 	if (!hdr)
274 		return -ENOMEM;
275 
276 	if (blk_dread(desc, blk_start, 1, hdr) != 1) {
277 		ret = -EIO;
278 		goto out;
279 	}
280 
281 	if (resource_check_header(hdr)) {
282 		printf("RESC: invalid\n");
283 		if (fdt_check_header(hdr)) {
284 			ret = -EINVAL;
285 			goto out;
286 		} else {
287 			/* this is a dtb file */
288 			printf("RESC: this is dtb\n");
289 			ret = resource_add_file(DEFAULT_DTB_FILE,
290 						fdt_totalsize(hdr),
291 						blk_start, 0, NULL, 0, false);
292 			goto out;
293 		}
294 	}
295 
296 	blk_cnt = hdr->e_blks * hdr->e_nums;
297 	hdr = realloc(hdr, (1 + blk_cnt) * desc->blksz);
298 	if (!hdr) {
299 		ret = -ENOMEM;
300 		goto out;
301 	}
302 
303 	buf = (void *)hdr + desc->blksz;
304 	if (blk_dread(desc, blk_start + hdr->c_offset, blk_cnt, buf) != blk_cnt) {
305 		ret = -EIO;
306 		goto out;
307 	}
308 
309 	resource_setup_list(desc, blk_start, hdr, false);
310 out:
311 	free(hdr);
312 
313 	return ret;
314 }
315 
resource_init(struct blk_desc * desc,disk_partition_t * part,ulong blk_offset)316 static int resource_init(struct blk_desc *desc,
317 			 disk_partition_t *part,
318 			 ulong blk_offset)
319 {
320 	printf("RESC: '%s', blk@0x%08lx\n", part->name, part->start + blk_offset);
321 
322 #ifdef CONFIG_ANDROID_AVB
323 	char hdr[512];
324 	ulong resc_buf = 0;
325 	int ret;
326 
327 	if (blk_dread(desc, part->start, 1, hdr) != 1)
328 		return -EIO;
329 
330 	/* only handle android boot/recovery.img and resource.img, ignore fit */
331 	if (!android_image_check_header((void *)hdr) ||
332 	    !resource_check_header((void *)hdr)) {
333 		ret = android_image_verify_resource((const char *)part->name, &resc_buf);
334 		if (ret) {
335 			printf("RESC: '%s', avb verify fail: %d\n", part->name, ret);
336 			return ret;
337 		}
338 
339 		/*
340 		 * unlock=0: resc_buf is valid and file was already full load in ram.
341 		 * unlock=1: resc_buf is 0.
342 		 */
343 		if (resc_buf && !resource_check_header((void *)resc_buf))
344 			return resource_setup_ram_list(desc, (void *)resc_buf);
345 	}
346 
347 #endif
348 
349 	return resource_setup_blk_list(desc, part->start + blk_offset);
350 }
351 
resource_default(struct blk_desc * desc,disk_partition_t * out_part,ulong * out_blk_offset)352 static int resource_default(struct blk_desc *desc,
353 			    disk_partition_t *out_part,
354 			    ulong *out_blk_offset)
355 {
356 	disk_partition_t part;
357 
358 	if (part_get_info_by_name(desc, PART_RESOURCE, &part) < 0)
359 		return -ENODEV;
360 
361 	*out_part = part;
362 	*out_blk_offset = 0;
363 
364 	return 0;
365 }
366 #endif
367 
resource_scan(void)368 static int resource_scan(void)
369 {
370 	struct blk_desc *desc = rockchip_get_bootdev();
371 	__maybe_unused int ret;
372 
373 	if (!desc) {
374 		printf("RESC: No bootdev\n");
375 		return -ENODEV;
376 	}
377 
378 	if (!list_empty(&entry_head))
379 		return 0;
380 
381 #ifdef CONFIG_ROCKCHIP_FIT_IMAGE
382 	ret = fit_image_init_resource(desc);
383 	if (!ret || ret != -EAGAIN)
384 		return ret;
385 #endif
386 #ifdef CONFIG_ROCKCHIP_UIMAGE
387 	ret = uimage_init_resource(desc);
388 	if (!ret || ret != -EAGAIN)
389 		return ret;
390 #endif
391 #ifdef CONFIG_ANDROID_BOOT_IMAGE
392 	disk_partition_t part;
393 	ulong blk_offset;
394 	char hdr[512];
395 	char name[32];
396 
397 	/* partition priority: boot/recovery > resource */
398 	if (!android_image_init_resource(desc, &part, &blk_offset)) {
399 		if (blk_dread(desc, part.start + blk_offset, 1, hdr) != 1)
400 			return -EIO;
401 
402 		if (resource_check_header((void *)hdr)) {
403 			strcpy(name, (char *)part.name);
404 			if (resource_default(desc, &part, &blk_offset))
405 				return -ENOENT;
406 
407 			printf("RESC: '%s' -> '%s'\n", name, part.name);
408 		}
409 	} else {
410 		if (resource_default(desc, &part, &blk_offset))
411 			return -ENOENT;
412 	}
413 
414 	/* now, 'part' can be boot/recovery/resource */
415 	return resource_init(desc, &part, blk_offset);
416 #endif
417 	return -ENOENT;
418 }
419 
resource_get_file(const char * name)420 static struct resource_file *resource_get_file(const char *name)
421 {
422 	struct resource_file *f;
423 	struct list_head *node;
424 
425 	if (resource_scan())
426 		return NULL;
427 
428 	list_for_each(node, &entry_head) {
429 		f = list_entry(node, struct resource_file, link);
430 		if (!strcmp(f->name, name))
431 			return f;
432 	}
433 
434 	return NULL;
435 }
436 
rockchip_read_resource_file(void * buf,const char * name,int blk_offset,int len)437 int rockchip_read_resource_file(void *buf, const char *name, int blk_offset, int len)
438 {
439 	struct blk_desc *desc = rockchip_get_bootdev();
440 	struct resource_file *f;
441 	int blk_cnt;
442 	ulong pos;
443 
444 	if (!desc)
445 		return -ENODEV;
446 
447 	f = resource_get_file(name);
448 	if (!f) {
449 		printf("No resource file: %s\n", name);
450 		return -ENOENT;
451 	}
452 
453 	if (len <= 0 || len > f->size)
454 		len = f->size;
455 
456 	if (f->in_ram) {
457 		pos = f->blk_start + (f->blk_offset + blk_offset) * desc->blksz;
458 		memcpy(buf, (char *)pos, len);
459 	} else {
460 		blk_cnt = DIV_ROUND_UP(len, desc->blksz);
461 		if (blk_dread(desc,
462 			      f->blk_start + f->blk_offset + blk_offset,
463 			      blk_cnt, buf) != blk_cnt)
464 			len = -EIO;
465 	}
466 
467 	return len;
468 }
469 
470 #ifdef CONFIG_ROCKCHIP_HWID_DTB
resource_read_hwid_dtb(void)471 static struct resource_file *resource_read_hwid_dtb(void)
472 {
473 	struct resource_file *file;
474 	struct list_head *node;
475 
476 	hwid_init_data();
477 
478 	list_for_each(node, &entry_head) {
479 		file = list_entry(node, struct resource_file, link);
480 		if (!strstr(file->name, DTB_SUFFIX))
481 			continue;
482 
483 		if (hwid_dtb_is_available(file->name))
484 			return file;
485 	}
486 
487 	return NULL;
488 }
489 #endif
490 
rockchip_read_resource_dtb(void * fdt_addr,char ** hash,int * hash_size)491 int rockchip_read_resource_dtb(void *fdt_addr, char **hash, int *hash_size)
492 {
493 	struct resource_file *f = NULL;
494 	int ret;
495 
496 #ifdef CONFIG_ROCKCHIP_HWID_DTB
497 	if (resource_scan())
498 		return -ENOENT;
499 
500 	f = resource_read_hwid_dtb();
501 #endif
502 	/* If no dtb match hardware id(GPIO/ADC), use the default */
503 	if (!f)
504 		f = resource_get_file(DEFAULT_DTB_FILE);
505 
506 	if (!f)
507 		return -ENODEV;
508 
509 	ret = rockchip_read_resource_file(fdt_addr, f->name, 0, 0);
510 	if (ret < 0)
511 		return ret;
512 
513 	if (fdt_check_header(fdt_addr))
514 		return -EBADF;
515 
516 	*hash = f->hash;
517 	*hash_size = f->hash_size;
518 
519 	printf("DTB: %s\n", f->name);
520 
521 	return 0;
522 }
523 
do_dump_resource(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])524 static int do_dump_resource(cmd_tbl_t *cmdtp, int flag,
525 			    int argc, char *const argv[])
526 {
527 	struct resource_file *f;
528 	struct list_head *node;
529 
530 	list_for_each(node, &entry_head) {
531 		f = list_entry(node, struct resource_file, link);
532 		resource_dump(f);
533 	}
534 
535 	return 0;
536 }
537 
538 U_BOOT_CMD(
539 	dump_resource, 1, 1, do_dump_resource,
540 	"dump resource files",
541 	""
542 );
543 
544