xref: /rk3399_rockchip-uboot/arch/arm/mach-rockchip/resource_img.c (revision c5ff44bc8ebec81979fbcd4aacb56e0ca3b2fb53)
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 
98 static int resource_check_header(struct resource_img_hdr *hdr)
99 {
100 	return memcmp(RESOURCE_MAGIC, hdr->magic, RESOURCE_MAGIC_SIZE);
101 }
102 
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 
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 
152 #ifdef CONFIG_ANDROID_BOOT_IMAGE
153 /*
154  * Add logo.bmp and logo_kernel.bmp from "logo" parititon
155  *
156  * Provide a "logo" partition for user to store logo.bmp and
157  * logo_kernel.bmp, so that the user can update them from
158  * kernel or user-space dynamically.
159  *
160  * "logo" partition layout, do not change order:
161  *
162  *   |----------------------| 0x00
163  *   | raw logo.bmp	    |
164  *   |----------------------| -> 512-byte aligned
165  *   | raw logo_kernel.bmp  |
166  *   |----------------------|
167  *
168  * N: the sector count of logo.bmp
169  *
170  * How to generate:
171  * 	cat logo.bmp > logo.img && truncate -s %512 logo.img && cat logo_kernel.bmp >> logo.img
172  */
173 static int resource_setup_logo_bmp(struct blk_desc *desc)
174 {
175 	struct bmp_header *header;
176 	const char *name[] = { "logo.bmp", "logo_kernel.bmp" };
177 	disk_partition_t part;
178 	u32 blk_offset = 0;
179 	u32 filesz;
180 	int ret, i;
181 
182 	if (part_get_info_by_name(desc, PART_LOGO, &part) < 0)
183 		return 0;
184 
185 	header = memalign(ARCH_DMA_MINALIGN, desc->blksz);
186 	if (!header)
187 		return -ENOMEM;
188 
189 	for (i = 0; i < ARRAY_SIZE(name); i++) {
190 		if (blk_dread(desc, part.start + blk_offset, 1, header) != 1) {
191 			ret = -EIO;
192 			break;
193 		}
194 
195 		if (header->signature[0] != 'B' || header->signature[1] != 'M') {
196 			ret = -EINVAL;
197 			break;
198 		}
199 
200 		filesz = get_unaligned_le32(&header->file_size);
201 		ret = resource_add_file(name[i], filesz, part.start, blk_offset,
202 					NULL, 0, false);
203 		if (ret)
204 			break;
205 
206 		/* move to next file */
207 		blk_offset += DIV_ROUND_UP(filesz, desc->blksz);
208 
209 		printf("LOGO: %s\n", name[i]);
210 
211 	}
212 
213 	free(header);
214 
215 	return ret;
216 }
217 #endif
218 
219 static int resource_setup_list(struct blk_desc *desc, ulong blk_start,
220 			       void *resc_hdr, bool in_ram)
221 {
222 	struct resource_img_hdr *hdr = resc_hdr;
223 	struct resource_entry *et;
224 	u32 i, stride;
225 	void *pos;
226 
227 	pos = (void *)hdr + hdr->c_offset * desc->blksz;
228 	stride = hdr->e_blks * desc->blksz;
229 
230 	for (i = 0; i < hdr->e_nums; i++) {
231 		et = pos + (i * stride);
232 		if (memcmp(et->tag, ENTRY_TAG, ENTRY_TAG_SIZE))
233 			continue;
234 
235 		resource_add_file(et->name, et->size,
236 				  blk_start, et->blk_offset,
237 				  et->hash, et->hash_size, in_ram);
238 	}
239 #ifdef CONFIG_ANDROID_BOOT_IMAGE
240 	resource_setup_logo_bmp(desc);
241 #endif
242 	return 0;
243 }
244 
245 int resource_setup_ram_list(struct blk_desc *desc, void *hdr)
246 {
247 	if (!desc)
248 		return -ENODEV;
249 
250 	if (resource_check_header(hdr)) {
251 		printf("RESC: invalid\n");
252 		return -EINVAL;
253 	}
254 
255 	/* @blk_start: set as 'hdr' point addr, to be used in byte */
256 	return resource_setup_list(desc, (ulong)hdr, hdr, true);
257 }
258 
259 #ifdef CONFIG_ANDROID_BOOT_IMAGE
260 static int resource_setup_blk_list(struct blk_desc *desc, ulong blk_start)
261 {
262 	struct resource_img_hdr *hdr;
263 	int blk_cnt;
264 	int ret = 0;
265 	void *buf;
266 
267 	hdr = memalign(ARCH_DMA_MINALIGN, desc->blksz);
268 	if (!hdr)
269 		return -ENOMEM;
270 
271 	if (blk_dread(desc, blk_start, 1, hdr) != 1) {
272 		ret = -EIO;
273 		goto out;
274 	}
275 
276 	if (resource_check_header(hdr)) {
277 		printf("RESC: invalid\n");
278 		if (fdt_check_header(hdr)) {
279 			ret = -EINVAL;
280 			goto out;
281 		} else {
282 			/* this is a dtb file */
283 			printf("RESC: this is dtb\n");
284 			ret = resource_add_file(DEFAULT_DTB_FILE,
285 						fdt_totalsize(hdr),
286 						blk_start, 0, NULL, 0, false);
287 			goto out;
288 		}
289 	}
290 
291 	blk_cnt = hdr->e_blks * hdr->e_nums;
292 	hdr = realloc(hdr, (1 + blk_cnt) * desc->blksz);
293 	if (!hdr) {
294 		ret = -ENOMEM;
295 		goto out;
296 	}
297 
298 	buf = (void *)hdr + desc->blksz;
299 	if (blk_dread(desc, blk_start + hdr->c_offset, blk_cnt, buf) != blk_cnt) {
300 		ret = -EIO;
301 		goto out;
302 	}
303 
304 	resource_setup_list(desc, blk_start, hdr, false);
305 out:
306 	free(hdr);
307 
308 	return ret;
309 }
310 
311 static int resource_init(struct blk_desc *desc,
312 			 disk_partition_t *part,
313 			 ulong blk_offset)
314 {
315 	printf("RESC: '%s', blk@0x%08lx\n", part->name, part->start + blk_offset);
316 
317 #ifdef CONFIG_ANDROID_AVB
318 	char hdr[512];
319 	ulong resc_buf = 0;
320 	int ret;
321 
322 	if (blk_dread(desc, part->start, 1, hdr) != 1)
323 		return -EIO;
324 
325 	/* only handle android boot/recovery.img and resource.img, ignore fit */
326 	if (!android_image_check_header((void *)hdr) ||
327 	    !resource_check_header((void *)hdr)) {
328 		ret = android_image_verify_resource((const char *)part->name, &resc_buf);
329 		if (ret) {
330 			printf("RESC: '%s', avb verify fail: %d\n", part->name, ret);
331 			return ret;
332 		}
333 
334 		/*
335 		 * unlock=0: resc_buf is valid and file was already full load in ram.
336 		 * unlock=1: resc_buf is 0.
337 		 */
338 		if (resc_buf && !resource_check_header((void *)resc_buf))
339 			return resource_setup_ram_list(desc, (void *)resc_buf);
340 	}
341 
342 #endif
343 
344 	return resource_setup_blk_list(desc, part->start + blk_offset);
345 }
346 
347 static int resource_default(struct blk_desc *desc,
348 			    disk_partition_t *out_part,
349 			    ulong *out_blk_offset)
350 {
351 	disk_partition_t part;
352 
353 	if (part_get_info_by_name(desc, PART_RESOURCE, &part) < 0)
354 		return -ENODEV;
355 
356 	*out_part = part;
357 	*out_blk_offset = 0;
358 
359 	return 0;
360 }
361 #endif
362 
363 static int resource_scan(void)
364 {
365 	struct blk_desc *desc = rockchip_get_bootdev();
366 	__maybe_unused int ret;
367 
368 	if (!desc) {
369 		printf("RESC: No bootdev\n");
370 		return -ENODEV;
371 	}
372 
373 	if (!list_empty(&entry_head))
374 		return 0;
375 
376 #ifdef CONFIG_ROCKCHIP_FIT_IMAGE
377 	ret = fit_image_init_resource(desc);
378 	if (!ret || ret != -EAGAIN)
379 		return ret;
380 #endif
381 #ifdef CONFIG_ROCKCHIP_UIMAGE
382 	ret = uimage_init_resource(desc);
383 	if (!ret || ret != -EAGAIN)
384 		return ret;
385 #endif
386 #ifdef CONFIG_ANDROID_BOOT_IMAGE
387 	disk_partition_t part;
388 	ulong blk_offset;
389 	char hdr[512];
390 	char name[32];
391 
392 	/* partition priority: boot/recovery > resource */
393 	if (!android_image_init_resource(desc, &part, &blk_offset)) {
394 		if (blk_dread(desc, part.start + blk_offset, 1, hdr) != 1)
395 			return -EIO;
396 
397 		if (resource_check_header((void *)hdr)) {
398 			strcpy(name, (char *)part.name);
399 			if (resource_default(desc, &part, &blk_offset))
400 				return -ENOENT;
401 
402 			printf("RESC: '%s' -> '%s'\n", name, part.name);
403 		}
404 	} else {
405 		if (resource_default(desc, &part, &blk_offset))
406 			return -ENOENT;
407 	}
408 
409 	/* now, 'part' can be boot/recovery/resource */
410 	return resource_init(desc, &part, blk_offset);
411 #endif
412 	return -ENOENT;
413 }
414 
415 static struct resource_file *resource_get_file(const char *name)
416 {
417 	struct resource_file *f;
418 	struct list_head *node;
419 
420 	if (resource_scan())
421 		return NULL;
422 
423 	list_for_each(node, &entry_head) {
424 		f = list_entry(node, struct resource_file, link);
425 		if (!strcmp(f->name, name))
426 			return f;
427 	}
428 
429 	return NULL;
430 }
431 
432 int rockchip_read_resource_file(void *buf, const char *name, int blk_offset, int len)
433 {
434 	struct blk_desc *desc = rockchip_get_bootdev();
435 	struct resource_file *f;
436 	int blk_cnt;
437 	ulong pos;
438 
439 	if (!desc)
440 		return -ENODEV;
441 
442 	f = resource_get_file(name);
443 	if (!f) {
444 		printf("No resource file: %s\n", name);
445 		return -ENOENT;
446 	}
447 
448 	if (len <= 0 || len > f->size)
449 		len = f->size;
450 
451 	if (f->in_ram) {
452 		pos = f->blk_start + (f->blk_offset + blk_offset) * desc->blksz;
453 		memcpy(buf, (char *)pos, len);
454 	} else {
455 		blk_cnt = DIV_ROUND_UP(len, desc->blksz);
456 		if (blk_dread(desc,
457 			      f->blk_start + f->blk_offset + blk_offset,
458 			      blk_cnt, buf) != blk_cnt)
459 			len = -EIO;
460 	}
461 
462 	return len;
463 }
464 
465 #ifdef CONFIG_ROCKCHIP_HWID_DTB
466 static struct resource_file *resource_read_hwid_dtb(void)
467 {
468 	struct resource_file *file;
469 	struct list_head *node;
470 
471 	hwid_init_data();
472 
473 	list_for_each(node, &entry_head) {
474 		file = list_entry(node, struct resource_file, link);
475 		if (!strstr(file->name, DTB_SUFFIX))
476 			continue;
477 
478 		if (hwid_dtb_is_available(file->name))
479 			return file;
480 	}
481 
482 	return NULL;
483 }
484 #endif
485 
486 int rockchip_read_resource_dtb(void *fdt_addr, char **hash, int *hash_size)
487 {
488 	struct resource_file *f = NULL;
489 	int ret;
490 
491 #ifdef CONFIG_ROCKCHIP_HWID_DTB
492 	if (resource_scan())
493 		return -ENOENT;
494 
495 	f = resource_read_hwid_dtb();
496 #endif
497 	/* If no dtb match hardware id(GPIO/ADC), use the default */
498 	if (!f)
499 		f = resource_get_file(DEFAULT_DTB_FILE);
500 
501 	if (!f)
502 		return -ENODEV;
503 
504 	ret = rockchip_read_resource_file(fdt_addr, f->name, 0, 0);
505 	if (ret < 0)
506 		return ret;
507 
508 	if (fdt_check_header(fdt_addr))
509 		return -EBADF;
510 
511 	*hash = f->hash;
512 	*hash_size = f->hash_size;
513 
514 	printf("DTB: %s\n", f->name);
515 
516 	return 0;
517 }
518 
519 static int do_dump_resource(cmd_tbl_t *cmdtp, int flag,
520 			    int argc, char *const argv[])
521 {
522 	struct resource_file *f;
523 	struct list_head *node;
524 
525 	list_for_each(node, &entry_head) {
526 		f = list_entry(node, struct resource_file, link);
527 		resource_dump(f);
528 	}
529 
530 	return 0;
531 }
532 
533 U_BOOT_CMD(
534 	dump_resource, 1, 1, do_dump_resource,
535 	"dump resource files",
536 	""
537 );
538 
539