1 /* 2 * (C) Copyright 2017 Rockchip Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 #include <common.h> 7 #include <adc.h> 8 #include <android_ab.h> 9 #include <android_bootloader.h> 10 #include <android_image.h> 11 #include <boot_rkimg.h> 12 #include <bmp_layout.h> 13 #include <malloc.h> 14 #include <asm/io.h> 15 #include <asm/unaligned.h> 16 #include <dm/ofnode.h> 17 #include <linux/list.h> 18 #include <asm/arch/fit.h> 19 #include <asm/arch/uimage.h> 20 #include <asm/arch/resource_img.h> 21 22 DECLARE_GLOBAL_DATA_PTR; 23 24 #define PART_RESOURCE "resource" 25 #define RESOURCE_MAGIC "RSCE" 26 #define RESOURCE_MAGIC_SIZE 4 27 #define RESOURCE_VERSION 0 28 #define CONTENT_VERSION 0 29 #define ENTRY_TAG "ENTR" 30 #define ENTRY_TAG_SIZE 4 31 #define MAX_FILE_NAME_LEN 220 32 #define MAX_HASH_LEN 32 33 #define DEFAULT_DTB_FILE "rk-kernel.dtb" 34 35 /* 36 * resource image structure 37 * ---------------------------------------------- 38 * | | 39 * | header (1 block) | 40 * | | 41 * ---------------------------------------------| 42 * | | | 43 * | entry0 (1 block) | | 44 * | | | 45 * ------------------------ | 46 * | | | 47 * | entry1 (1 block) | contents (n blocks) | 48 * | | | 49 * ------------------------ | 50 * | ...... | | 51 * ------------------------ | 52 * | | | 53 * | entryn (1 block) | | 54 * | | | 55 * ---------------------------------------------- 56 * | | 57 * | file0 (x blocks) | 58 * | | 59 * ---------------------------------------------- 60 * | | 61 * | file1 (y blocks) | 62 * | | 63 * ---------------------------------------------- 64 * | ...... | 65 * |--------------------------------------------- 66 * | | 67 * | filen (z blocks) | 68 * | | 69 * ---------------------------------------------- 70 */ 71 72 /** 73 * struct resource_image_header 74 * 75 * @magic: should be "RSCE" 76 * @version: resource image version, current is 0 77 * @c_version: content version, current is 0 78 * @blks: the size of the header ( 1 block = 512 bytes) 79 * @c_offset: contents offset(by block) in the image 80 * @e_blks: the size(by block) of the entry in the contents 81 * @e_num: numbers of the entrys. 82 */ 83 84 struct resource_img_hdr { 85 char magic[4]; 86 uint16_t version; 87 uint16_t c_version; 88 uint8_t blks; 89 uint8_t c_offset; 90 uint8_t e_blks; 91 uint32_t e_nums; 92 }; 93 94 struct resource_entry { 95 char tag[4]; 96 char name[MAX_FILE_NAME_LEN]; 97 char hash[MAX_HASH_LEN]; 98 uint32_t hash_size; 99 uint32_t f_offset; /* Sector offset */ 100 uint32_t f_size; /* Bytes */ 101 }; 102 103 LIST_HEAD(entrys_head); 104 LIST_HEAD(entrys_dtbs_head); 105 106 __weak int board_resource_dtb_accepted(char *dtb_name) 107 { 108 return 1; 109 } 110 111 int resource_image_check_header(void *rsce_hdr) 112 { 113 struct resource_img_hdr *hdr = rsce_hdr; 114 int ret; 115 116 ret = memcmp(RESOURCE_MAGIC, hdr->magic, RESOURCE_MAGIC_SIZE); 117 if (ret) { 118 debug("bad resource image magic: %s\n", 119 hdr->magic ? hdr->magic : "none"); 120 ret = -EINVAL; 121 } 122 123 debug("resource image header:\n"); 124 debug("magic:%s\n", hdr->magic); 125 debug("version:%d\n", hdr->version); 126 debug("c_version:%d\n", hdr->c_version); 127 debug("blks:%d\n", hdr->blks); 128 debug("c_offset:%d\n", hdr->c_offset); 129 debug("e_blks:%d\n", hdr->e_blks); 130 debug("e_num:%d\n", hdr->e_nums); 131 132 return ret; 133 } 134 135 static int add_file_to_list(struct resource_entry *entry, int rsce_base, bool ram) 136 { 137 struct resource_file *file; 138 139 if (memcmp(entry->tag, ENTRY_TAG, ENTRY_TAG_SIZE)) { 140 debug("invalid entry tag\n"); 141 return -ENOENT; 142 } 143 144 file = malloc(sizeof(*file)); 145 if (!file) { 146 debug("out of memory\n"); 147 return -ENOMEM; 148 } 149 150 strcpy(file->name, entry->name); 151 file->rsce_base = rsce_base; 152 file->f_offset = entry->f_offset; 153 file->f_size = entry->f_size; 154 file->hash_size = entry->hash_size; 155 file->ram = ram; 156 memcpy(file->hash, entry->hash, entry->hash_size); 157 list_add_tail(&file->link, &entrys_head); 158 if (strstr(file->name, DTB_SUFFIX) && board_resource_dtb_accepted(file->name)) 159 list_add_tail(&file->dtbs, &entrys_dtbs_head); 160 debug("ENTRY: addr: %p, name: %18s, base: 0x%08x, offset: 0x%08x, size: 0x%08x\n", 161 entry, file->name, file->rsce_base, file->f_offset, file->f_size); 162 163 return 0; 164 } 165 166 int resource_replace_entry(const char *f_name, uint32_t base, 167 uint32_t f_offset, uint32_t f_size) 168 { 169 struct resource_entry *entry; 170 struct resource_file *file; 171 struct list_head *node; 172 173 if (!f_name || !f_size) 174 return -EINVAL; 175 176 entry = calloc(1, sizeof(*entry)); 177 if (!entry) 178 return -ENOMEM; 179 180 strcpy(entry->tag, ENTRY_TAG); 181 strcpy(entry->name, f_name); 182 entry->f_offset = f_offset; 183 entry->f_size = f_size; 184 entry->hash_size = 0; 185 186 /* Delete exist entry, then add this new */ 187 list_for_each(node, &entrys_head) { 188 file = list_entry(node, struct resource_file, link); 189 if (!strcmp(file->name, entry->name)) { 190 list_del(&file->link); 191 free(file); 192 break; 193 } 194 } 195 196 add_file_to_list(entry, base, false); 197 free(entry); 198 199 return 0; 200 } 201 202 int resource_create_ram_list(struct blk_desc *dev_desc, void *rsce_hdr) 203 { 204 struct resource_img_hdr *hdr = rsce_hdr; 205 struct resource_entry *entry; 206 int e_num, size; 207 void *data; 208 int ret = 0; 209 210 if (resource_image_check_header(hdr)) { 211 ret = -EINVAL; 212 goto out; 213 } 214 215 list_del_init(&entrys_head); 216 list_del_init(&entrys_dtbs_head); 217 data = (void *)((ulong)hdr + hdr->c_offset * dev_desc->blksz); 218 for (e_num = 0; e_num < hdr->e_nums; e_num++) { 219 size = e_num * hdr->e_blks * dev_desc->blksz; 220 entry = (struct resource_entry *)(data + size); 221 add_file_to_list(entry, (ulong)hdr, true); 222 } 223 out: 224 resource_read_logo_bmps(); 225 226 return ret; 227 } 228 229 static int resource_create_list(struct blk_desc *dev_desc, int rsce_base) 230 { 231 struct resource_img_hdr *hdr; 232 struct resource_entry *entry; 233 int blknum, e_num; 234 void *data = NULL; 235 int ret = 0; 236 int size; 237 238 hdr = memalign(ARCH_DMA_MINALIGN, dev_desc->blksz); 239 if (!hdr) 240 return -ENOMEM; 241 242 if (blk_dread(dev_desc, rsce_base, 1, hdr) != 1) { 243 printf("Failed to read resource hdr\n"); 244 ret = -EIO; 245 goto err; 246 } 247 248 if (resource_image_check_header(hdr)) { 249 if (fdt_check_header(hdr)) { 250 printf("No valid resource or dtb file\n"); 251 ret = -EINVAL; 252 goto err; 253 } else { 254 free(hdr); 255 return resource_replace_entry(DEFAULT_DTB_FILE, rsce_base, 256 0, fdt_totalsize(hdr)); 257 } 258 } 259 260 blknum = hdr->e_blks * hdr->e_nums; 261 data = memalign(ARCH_DMA_MINALIGN, blknum * dev_desc->blksz); 262 if (!data) { 263 ret = -ENOMEM; 264 goto err; 265 } 266 267 if (blk_dread(dev_desc, rsce_base + hdr->c_offset, 268 blknum, data) != blknum) { 269 printf("Failed to read resource entries\n"); 270 ret = -EIO; 271 goto err; 272 } 273 274 /* 275 * Add all file into resource file list, and load what we want from 276 * storage when we really need it. 277 */ 278 for (e_num = 0; e_num < hdr->e_nums; e_num++) { 279 size = e_num * hdr->e_blks * dev_desc->blksz; 280 entry = (struct resource_entry *)(data + size); 281 add_file_to_list(entry, rsce_base, false); 282 } 283 284 err: 285 if (data) 286 free(data); 287 if (hdr) 288 free(hdr); 289 290 resource_read_logo_bmps(); 291 292 return ret; 293 } 294 295 static int read_dtb_from_android(struct blk_desc *dev_desc, 296 struct andr_img_hdr *hdr, 297 ulong rsce_base) 298 { 299 ulong dtb_offset = 0; 300 ulong dtb_size = 0; 301 302 if (!hdr || hdr->header_version <= 1) { 303 return 0; 304 } else if (hdr->header_version == 2) { 305 dtb_offset += hdr->page_size; 306 dtb_offset += ALIGN(hdr->kernel_size, hdr->page_size); 307 dtb_offset += ALIGN(hdr->ramdisk_size, hdr->page_size); 308 dtb_offset += ALIGN(hdr->recovery_dtbo_size, hdr->page_size) + 309 ALIGN(hdr->second_size, hdr->page_size); 310 dtb_size = hdr->dtb_size; 311 } else if (hdr->header_version >= 3) { 312 ulong vendor_boot_hdr_size = (hdr->header_version == 3) ? 313 VENDOR_BOOT_HDRv3_SIZE : VENDOR_BOOT_HDRv4_SIZE; 314 315 dtb_offset += ALIGN(vendor_boot_hdr_size, 316 hdr->vendor_page_size) + 317 ALIGN(hdr->vendor_ramdisk_size, 318 hdr->vendor_page_size); 319 dtb_size = hdr->dtb_size; 320 } 321 322 if (!dtb_size) 323 return 0; 324 325 /* 326 * boot_img_hdr_v234 feature. 327 * 328 * If dtb position is present, replace the old with new one if 329 * we don't need to verify DTB hash from resource.img file entry. 330 */ 331 dtb_offset = DIV_ROUND_UP(dtb_offset, dev_desc->blksz); 332 #ifndef CONFIG_ROCKCHIP_DTB_VERIFY 333 if (replace_resource_entry(DEFAULT_DTB_FILE, rsce_base, dtb_offset, dtb_size)) 334 printf("Failed to load dtb from v2 dtb position\n"); 335 else 336 #endif 337 env_update("bootargs", "androidboot.dtb_idx=0"); 338 339 return 0; 340 } 341 342 static int get_resource_base_sector(struct blk_desc *dev_desc, 343 struct andr_img_hdr **ret_hdr) 344 { 345 disk_partition_t part; 346 int rsce_base = 0; 347 #ifdef CONFIG_ANDROID_BOOT_IMAGE 348 struct andr_img_hdr *hdr; 349 u32 os_ver = 0, os_lvl; 350 const char *part_boot = PART_BOOT; 351 352 /* 353 * Anyway, we must read android hdr firstly from boot/recovery partition 354 * to get the 'os_version' for android_bcb_msg_sector_offset(), in order 355 * to confirm BCB message offset of *MISC* partition. 356 */ 357 #ifdef CONFIG_ANDROID_AB 358 part_boot = ab_can_find_recovery_part() ? PART_RECOVERY : PART_BOOT; 359 #endif 360 361 if (part_get_info_by_name(dev_desc, part_boot, &part) < 0) 362 goto resource_part; 363 364 hdr = populate_andr_img_hdr(dev_desc, &part); 365 if (hdr) { 366 os_ver = hdr->os_version >> 11; 367 os_lvl = hdr->os_version & ((1U << 11) - 1); 368 if (os_ver) 369 gd->bd->bi_andr_version = hdr->os_version; 370 } 371 372 #ifndef CONFIG_ANDROID_AB 373 /* Get boot mode from misc and read if recovery mode */ 374 if (rockchip_get_boot_mode() == BOOT_MODE_RECOVERY) { 375 if (hdr) 376 free(hdr); 377 378 if (part_get_info_by_name(dev_desc, PART_RECOVERY, &part) < 0) 379 goto resource_part; 380 381 hdr = populate_andr_img_hdr(dev_desc, &part); 382 if (!hdr) 383 goto resource_part; 384 } 385 #endif 386 /* If Android v012, getting resource from second position ! */ 387 if (hdr) { 388 if (os_ver) 389 printf("Android %u.%u, Build %u.%u, v%d\n", 390 (os_ver >> 14) & 0x7F, (os_ver >> 7) & 0x7F, 391 (os_lvl >> 4) + 2000, os_lvl & 0x0F, 392 hdr->header_version); 393 *ret_hdr = hdr; 394 if (hdr->header_version < 3) { 395 rsce_base = part.start * dev_desc->blksz; 396 rsce_base += hdr->page_size; 397 rsce_base += ALIGN(hdr->kernel_size, hdr->page_size); 398 rsce_base += ALIGN(hdr->ramdisk_size, hdr->page_size); 399 rsce_base = DIV_ROUND_UP(rsce_base, dev_desc->blksz); 400 goto finish; 401 } 402 } 403 resource_part: 404 #endif 405 /* resource partition */ 406 if (part_get_info_by_name(dev_desc, PART_RESOURCE, &part) < 0) { 407 printf("No resource partition\n"); 408 return -ENODEV; 409 } else { 410 rsce_base = part.start; 411 } 412 #ifdef CONFIG_ANDROID_BOOT_IMAGE 413 finish: 414 #endif 415 printf("Found DTB in %s part\n", part.name); 416 417 return rsce_base; 418 } 419 420 /* 421 * There are: logo/battery pictures and dtb file in the resource image by default. 422 * 423 * This function does: 424 * 425 * 1. Get resource image base sector from: boot/recovery(AOSP) > resource(RK) 426 * 2. Create resource files list(addition: add logo bmps) 427 * 3. Add dtb from android v2 dtb pos, override the old one from resource file 428 */ 429 int resource_init_list(void) 430 { 431 struct andr_img_hdr *hdr = NULL; 432 struct blk_desc *dev_desc; 433 int rsce_base; 434 435 dev_desc = rockchip_get_bootdev(); 436 if (!dev_desc) { 437 printf("No dev_desc!\n"); 438 return -ENODEV; 439 } 440 441 rsce_base = get_resource_base_sector(dev_desc, &hdr); 442 if (rsce_base > 0) { 443 if (resource_create_list(dev_desc, rsce_base)) 444 printf("Failed to create resource list\n"); 445 } 446 447 /* override the resource dtb with android dtb if need */ 448 return read_dtb_from_android(dev_desc, hdr, rsce_base); 449 } 450 451 int resource_is_empty(void) 452 { 453 return list_empty(&entrys_head); 454 } 455 456 static struct resource_file *get_file_info(const char *name) 457 { 458 struct resource_file *file; 459 struct list_head *node; 460 461 if (list_empty(&entrys_head)) { 462 if (resource_init_list()) 463 return NULL; 464 } 465 466 list_for_each(node, &entrys_head) { 467 file = list_entry(node, struct resource_file, link); 468 if (!strcmp(file->name, name)) 469 return file; 470 } 471 472 return NULL; 473 } 474 475 /* 476 * read file from resource partition 477 * @buf: destination buf to store file data; 478 * @name: file name 479 * @offset: blocks offset in the file, 1 block = 512 bytes 480 * @len: the size(by bytes) of file to read. 481 */ 482 int rockchip_read_resource_file(void *buf, const char *name, int offset, int len) 483 { 484 struct resource_file *file; 485 struct blk_desc *dev_desc; 486 int ret = 0; 487 int blks; 488 ulong src; 489 490 file = get_file_info(name); 491 if (!file) { 492 printf("No file: %s\n", name); 493 return -ENOENT; 494 } 495 496 dev_desc = rockchip_get_bootdev(); 497 if (!dev_desc) { 498 printf("No dev_desc!\n"); 499 return -ENODEV; 500 } 501 502 if (len <= 0 || len > file->f_size) 503 len = file->f_size; 504 505 if (file->ram) { 506 src = file->rsce_base + 507 (file->f_offset + offset) * dev_desc->blksz; 508 memcpy(buf, (char *)src, len); 509 ret = len; 510 } else { 511 blks = DIV_ROUND_UP(len, dev_desc->blksz); 512 ret = blk_dread(dev_desc, 513 file->rsce_base + file->f_offset + offset, 514 blks, buf); 515 ret = (ret != blks) ? -EIO : len; 516 } 517 518 return ret; 519 } 520 521 static struct resource_file *get_default_dtb(void) 522 { 523 struct resource_file *target_file = NULL; 524 struct resource_file *file; 525 struct list_head *node; 526 int num = 0; 527 528 if (list_empty(&entrys_head)) { 529 if (resource_init_list()) 530 return NULL; 531 } 532 533 list_for_each(node, &entrys_dtbs_head) { 534 num++; 535 file = list_entry(node, struct resource_file, dtbs); 536 if (strcmp(file->name, DEFAULT_DTB_FILE)) 537 target_file = file; 538 } 539 540 /* 541 * two possible case: 542 * case 1. rk-kernel.dtb only 543 * case 2. targe_file(s) + rk-kernel.dtb(maybe they are the same), 544 * use (last)target_file as result one. 545 */ 546 if (num > 2) 547 printf("Error: find duplicate(%d) dtbs\n", num); 548 549 return target_file ? : get_file_info(DEFAULT_DTB_FILE); 550 } 551 552 int rockchip_read_resource_dtb(void *fdt_addr, char **hash, int *hash_size) 553 { 554 struct resource_file *file = NULL; 555 int ret; 556 557 #ifdef CONFIG_ROCKCHIP_HWID_DTB 558 file = resource_read_hwid_dtb(); 559 #endif 560 /* If no dtb matches hardware id(GPIO/ADC), use the default */ 561 if (!file) 562 file = get_default_dtb(); 563 564 if (!file) 565 return -ENODEV; 566 567 ret = rockchip_read_resource_file(fdt_addr, file->name, 0, 0); 568 if (ret < 0) 569 return ret; 570 571 if (fdt_check_header(fdt_addr)) 572 return -EBADF; 573 574 *hash = file->hash; 575 *hash_size = file->hash_size; 576 printf("DTB: %s\n", file->name); 577 578 return 0; 579 } 580 581 int resource_populate_dtb(void *img, void *fdt) 582 { 583 struct blk_desc *dev_desc; 584 int format; 585 int ret; 586 587 format = (genimg_get_format(img)); 588 #ifdef CONFIG_ANDROID_BOOT_IMAGE 589 if (format == IMAGE_FORMAT_ANDROID) { 590 struct andr_img_hdr *hdr = img; 591 ulong offset; 592 593 dev_desc = rockchip_get_bootdev(); 594 if (!dev_desc) 595 return -ENODEV; 596 597 offset = hdr->page_size + ALIGN(hdr->kernel_size, hdr->page_size) + 598 ALIGN(hdr->ramdisk_size, hdr->page_size); 599 ret = resource_create_ram_list(dev_desc, (void *)hdr + offset); 600 if (ret) 601 return ret; 602 603 return rockchip_read_dtb_file((void *)fdt); 604 } 605 #endif 606 #if IMAGE_ENABLE_FIT 607 if (format == IMAGE_FORMAT_FIT) { 608 const void *data; 609 size_t size; 610 int noffset; 611 612 noffset = fdt_path_offset(img, "/images/resource"); 613 if (noffset < 0) 614 return noffset; 615 616 ret = fit_image_get_data(img, noffset, &data, &size); 617 if (ret < 0) 618 return ret; 619 620 dev_desc = rockchip_get_bootdev(); 621 if (!dev_desc) 622 return -ENODEV; 623 624 ret = resource_create_ram_list(dev_desc, (void *)data); 625 if (ret) { 626 printf("resource_create_ram_list fail, ret=%d\n", ret); 627 return ret; 628 } 629 630 return rockchip_read_dtb_file((void *)fdt); 631 } 632 #endif 633 634 return -EINVAL; 635 } 636 637 int resource_traverse_init_list(void) 638 { 639 if (!resource_is_empty()) 640 return 0; 641 642 #ifdef CONFIG_ROCKCHIP_FIT_IMAGE 643 if (!fit_image_init_resource()) 644 return 0; 645 #endif 646 #ifdef CONFIG_ROCKCHIP_UIMAGE 647 if (!uimage_init_resource()) 648 return 0; 649 #endif 650 /* Android image is default supported within resource core */ 651 652 return 0; 653 } 654 655 static int do_dump_resource(cmd_tbl_t *cmdtp, int flag, 656 int argc, char *const argv[]) 657 { 658 struct resource_file *file; 659 struct list_head *node; 660 661 printf("Resources:\n"); 662 list_for_each(node, &entrys_head) { 663 file = list_entry(node, struct resource_file, link); 664 printf(" %s: 0x%08x(sector), 0x%08x(bytes)\n", 665 file->name, file->rsce_base + file->f_offset, file->f_size); 666 } 667 668 #ifdef CONFIG_ROCKCHIP_HWID_DTB 669 printf("DTBs:\n"); 670 list_for_each(node, &entrys_dtbs_head) { 671 file = list_entry(node, struct resource_file, dtbs); 672 printf(" %s: 0x%08x(sector),0x%08x(bytes)\n", 673 file->name, file->rsce_base + file->f_offset, file->f_size); 674 } 675 #endif 676 return 0; 677 } 678 679 U_BOOT_CMD( 680 dump_resource, 1, 1, do_dump_resource, 681 "dump resource list", 682 "" 683 ); 684 685