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