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