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