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 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 */ 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 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 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 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 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 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 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 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 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 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 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 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