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