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 list_del(&file->dtbs); 192 free(file); 193 break; 194 } 195 } 196 197 add_file_to_list(entry, base, false); 198 free(entry); 199 200 return 0; 201 } 202 203 int resource_create_ram_list(struct blk_desc *dev_desc, void *rsce_hdr) 204 { 205 struct resource_img_hdr *hdr = rsce_hdr; 206 struct resource_entry *entry; 207 int e_num, size; 208 void *data; 209 int ret = 0; 210 211 if (resource_image_check_header(hdr)) { 212 ret = -EINVAL; 213 goto out; 214 } 215 216 list_del_init(&entrys_head); 217 list_del_init(&entrys_dtbs_head); 218 data = (void *)((ulong)hdr + hdr->c_offset * dev_desc->blksz); 219 for (e_num = 0; e_num < hdr->e_nums; e_num++) { 220 size = e_num * hdr->e_blks * dev_desc->blksz; 221 entry = (struct resource_entry *)(data + size); 222 add_file_to_list(entry, (ulong)hdr, true); 223 } 224 out: 225 resource_read_logo_bmps(); 226 227 return ret; 228 } 229 230 static int resource_create_list(struct blk_desc *dev_desc, int rsce_base) 231 { 232 struct resource_img_hdr *hdr; 233 struct resource_entry *entry; 234 int blknum, e_num; 235 void *data = NULL; 236 int ret = 0; 237 int size; 238 239 hdr = memalign(ARCH_DMA_MINALIGN, dev_desc->blksz); 240 if (!hdr) 241 return -ENOMEM; 242 243 if (blk_dread(dev_desc, rsce_base, 1, hdr) != 1) { 244 printf("Failed to read resource hdr\n"); 245 ret = -EIO; 246 goto err; 247 } 248 249 if (resource_image_check_header(hdr)) { 250 if (fdt_check_header(hdr)) { 251 printf("No valid resource or dtb file\n"); 252 ret = -EINVAL; 253 goto err; 254 } else { 255 free(hdr); 256 return resource_replace_entry(DEFAULT_DTB_FILE, rsce_base, 257 0, fdt_totalsize(hdr)); 258 } 259 } 260 261 blknum = hdr->e_blks * hdr->e_nums; 262 data = memalign(ARCH_DMA_MINALIGN, blknum * dev_desc->blksz); 263 if (!data) { 264 ret = -ENOMEM; 265 goto err; 266 } 267 268 if (blk_dread(dev_desc, rsce_base + hdr->c_offset, 269 blknum, data) != blknum) { 270 printf("Failed to read resource entries\n"); 271 ret = -EIO; 272 goto err; 273 } 274 275 /* 276 * Add all file into resource file list, and load what we want from 277 * storage when we really need it. 278 */ 279 for (e_num = 0; e_num < hdr->e_nums; e_num++) { 280 size = e_num * hdr->e_blks * dev_desc->blksz; 281 entry = (struct resource_entry *)(data + size); 282 add_file_to_list(entry, rsce_base, false); 283 } 284 285 err: 286 if (data) 287 free(data); 288 if (hdr) 289 free(hdr); 290 291 resource_read_logo_bmps(); 292 293 return ret; 294 } 295 296 static int read_dtb_from_android(struct blk_desc *dev_desc, 297 struct andr_img_hdr *hdr, 298 ulong rsce_base) 299 { 300 ulong dtb_offset = 0; 301 ulong dtb_size = 0; 302 303 if (!hdr || hdr->header_version <= 1) { 304 return 0; 305 } else if (hdr->header_version == 2) { 306 dtb_offset += hdr->page_size; 307 dtb_offset += ALIGN(hdr->kernel_size, hdr->page_size); 308 dtb_offset += ALIGN(hdr->ramdisk_size, hdr->page_size); 309 dtb_offset += ALIGN(hdr->recovery_dtbo_size, hdr->page_size) + 310 ALIGN(hdr->second_size, hdr->page_size); 311 dtb_size = hdr->dtb_size; 312 } else if (hdr->header_version >= 3) { 313 ulong vendor_boot_hdr_size = (hdr->header_version == 3) ? 314 VENDOR_BOOT_HDRv3_SIZE : VENDOR_BOOT_HDRv4_SIZE; 315 316 dtb_offset += ALIGN(vendor_boot_hdr_size, 317 hdr->vendor_page_size) + 318 ALIGN(hdr->vendor_ramdisk_size, 319 hdr->vendor_page_size); 320 dtb_size = hdr->dtb_size; 321 } 322 323 if (!dtb_size) 324 return 0; 325 326 /* 327 * boot_img_hdr_v234 feature. 328 * 329 * If dtb position is present, replace the old with new one if 330 * we don't need to verify DTB hash from resource.img file entry. 331 */ 332 dtb_offset = DIV_ROUND_UP(dtb_offset, dev_desc->blksz); 333 #ifndef CONFIG_ROCKCHIP_DTB_VERIFY 334 if (replace_resource_entry(DEFAULT_DTB_FILE, rsce_base, dtb_offset, dtb_size)) 335 printf("Failed to load dtb from v2 dtb position\n"); 336 else 337 #endif 338 env_update("bootargs", "androidboot.dtb_idx=0"); 339 340 return 0; 341 } 342 343 static int get_resource_base_sector(struct blk_desc *dev_desc, 344 struct andr_img_hdr **ret_hdr) 345 { 346 disk_partition_t part; 347 int rsce_base = 0; 348 #ifdef CONFIG_ANDROID_BOOT_IMAGE 349 struct andr_img_hdr *hdr; 350 u32 os_ver = 0, os_lvl; 351 const char *part_boot = PART_BOOT; 352 353 /* 354 * Anyway, we must read android hdr firstly from boot/recovery partition 355 * to get the 'os_version' for android_bcb_msg_sector_offset(), in order 356 * to confirm BCB message offset of *MISC* partition. 357 */ 358 #ifdef CONFIG_ANDROID_AB 359 part_boot = ab_can_find_recovery_part() ? PART_RECOVERY : PART_BOOT; 360 #endif 361 362 if (part_get_info_by_name(dev_desc, part_boot, &part) < 0) 363 goto resource_part; 364 365 hdr = populate_andr_img_hdr(dev_desc, &part); 366 if (hdr) { 367 os_ver = hdr->os_version >> 11; 368 os_lvl = hdr->os_version & ((1U << 11) - 1); 369 if (os_ver) 370 gd->bd->bi_andr_version = hdr->os_version; 371 } 372 373 #ifndef CONFIG_ANDROID_AB 374 /* Get boot mode from misc and read if recovery mode */ 375 if (rockchip_get_boot_mode() == BOOT_MODE_RECOVERY) { 376 if (hdr) 377 free(hdr); 378 379 if (part_get_info_by_name(dev_desc, PART_RECOVERY, &part) < 0) 380 goto resource_part; 381 382 hdr = populate_andr_img_hdr(dev_desc, &part); 383 if (!hdr) 384 goto resource_part; 385 } 386 #endif 387 /* If Android v012, getting resource from second position ! */ 388 if (hdr) { 389 if (os_ver) 390 printf("Android %u.%u, Build %u.%u, v%d\n", 391 (os_ver >> 14) & 0x7F, (os_ver >> 7) & 0x7F, 392 (os_lvl >> 4) + 2000, os_lvl & 0x0F, 393 hdr->header_version); 394 *ret_hdr = hdr; 395 if (hdr->header_version < 3) { 396 rsce_base = part.start * dev_desc->blksz; 397 rsce_base += hdr->page_size; 398 rsce_base += ALIGN(hdr->kernel_size, hdr->page_size); 399 rsce_base += ALIGN(hdr->ramdisk_size, hdr->page_size); 400 rsce_base = DIV_ROUND_UP(rsce_base, dev_desc->blksz); 401 goto finish; 402 } 403 } 404 resource_part: 405 #endif 406 /* resource partition */ 407 if (part_get_info_by_name(dev_desc, PART_RESOURCE, &part) < 0) { 408 printf("No resource partition\n"); 409 return -ENODEV; 410 } else { 411 rsce_base = part.start; 412 } 413 #ifdef CONFIG_ANDROID_BOOT_IMAGE 414 finish: 415 #endif 416 printf("Found DTB in %s part\n", part.name); 417 418 return rsce_base; 419 } 420 421 /* 422 * There are: logo/battery pictures and dtb file in the resource image by default. 423 * 424 * This function does: 425 * 426 * 1. Get resource image base sector from: boot/recovery(AOSP) > resource(RK) 427 * 2. Create resource files list(addition: add logo bmps) 428 * 3. Add dtb from android v2 dtb pos, override the old one from resource file 429 */ 430 int resource_init_list(void) 431 { 432 struct andr_img_hdr *hdr = NULL; 433 struct blk_desc *dev_desc; 434 int rsce_base; 435 436 dev_desc = rockchip_get_bootdev(); 437 if (!dev_desc) { 438 printf("No dev_desc!\n"); 439 return -ENODEV; 440 } 441 442 rsce_base = get_resource_base_sector(dev_desc, &hdr); 443 if (rsce_base > 0) { 444 if (resource_create_list(dev_desc, rsce_base)) 445 printf("Failed to create resource list\n"); 446 } 447 448 /* override the resource dtb with android dtb if need */ 449 return read_dtb_from_android(dev_desc, hdr, rsce_base); 450 } 451 452 int resource_is_empty(void) 453 { 454 return list_empty(&entrys_head); 455 } 456 457 static struct resource_file *get_file_info(const char *name) 458 { 459 struct resource_file *file; 460 struct list_head *node; 461 462 if (list_empty(&entrys_head)) { 463 if (resource_init_list()) 464 return NULL; 465 } 466 467 list_for_each(node, &entrys_head) { 468 file = list_entry(node, struct resource_file, link); 469 if (!strcmp(file->name, name)) 470 return file; 471 } 472 473 return NULL; 474 } 475 476 /* 477 * read file from resource partition 478 * @buf: destination buf to store file data; 479 * @name: file name 480 * @offset: blocks offset in the file, 1 block = 512 bytes 481 * @len: the size(by bytes) of file to read. 482 */ 483 int rockchip_read_resource_file(void *buf, const char *name, int offset, int len) 484 { 485 struct resource_file *file; 486 struct blk_desc *dev_desc; 487 int ret = 0; 488 int blks; 489 ulong src; 490 491 file = get_file_info(name); 492 if (!file) { 493 printf("No file: %s\n", name); 494 return -ENOENT; 495 } 496 497 dev_desc = rockchip_get_bootdev(); 498 if (!dev_desc) { 499 printf("No dev_desc!\n"); 500 return -ENODEV; 501 } 502 503 if (len <= 0 || len > file->f_size) 504 len = file->f_size; 505 506 if (file->ram) { 507 src = file->rsce_base + 508 (file->f_offset + offset) * dev_desc->blksz; 509 memcpy(buf, (char *)src, len); 510 ret = len; 511 } else { 512 blks = DIV_ROUND_UP(len, dev_desc->blksz); 513 ret = blk_dread(dev_desc, 514 file->rsce_base + file->f_offset + offset, 515 blks, buf); 516 ret = (ret != blks) ? -EIO : len; 517 } 518 519 return ret; 520 } 521 522 static struct resource_file *get_default_dtb(void) 523 { 524 struct resource_file *target_file = NULL; 525 struct resource_file *file; 526 struct list_head *node; 527 int num = 0; 528 529 if (list_empty(&entrys_head)) { 530 if (resource_init_list()) 531 return NULL; 532 } 533 534 list_for_each(node, &entrys_dtbs_head) { 535 num++; 536 file = list_entry(node, struct resource_file, dtbs); 537 if (strcmp(file->name, DEFAULT_DTB_FILE)) 538 target_file = file; 539 } 540 541 /* 542 * two possible case: 543 * case 1. rk-kernel.dtb only 544 * case 2. targe_file(s) + rk-kernel.dtb(maybe they are the same), 545 * use (last)target_file as result one. 546 */ 547 if (num > 2) 548 printf("Error: find duplicate(%d) dtbs\n", num); 549 550 return target_file ? : get_file_info(DEFAULT_DTB_FILE); 551 } 552 553 int rockchip_read_resource_dtb(void *fdt_addr, char **hash, int *hash_size) 554 { 555 struct resource_file *file = NULL; 556 int ret; 557 558 #ifdef CONFIG_ROCKCHIP_HWID_DTB 559 file = resource_read_hwid_dtb(); 560 #endif 561 /* If no dtb matches hardware id(GPIO/ADC), use the default */ 562 if (!file) 563 file = get_default_dtb(); 564 565 if (!file) 566 return -ENODEV; 567 568 ret = rockchip_read_resource_file(fdt_addr, file->name, 0, 0); 569 if (ret < 0) 570 return ret; 571 572 if (fdt_check_header(fdt_addr)) 573 return -EBADF; 574 575 *hash = file->hash; 576 *hash_size = file->hash_size; 577 printf("DTB: %s\n", file->name); 578 579 return 0; 580 } 581 582 int resource_populate_dtb(void *img, void *fdt) 583 { 584 struct blk_desc *dev_desc; 585 int format; 586 int ret; 587 588 format = (genimg_get_format(img)); 589 #ifdef CONFIG_ANDROID_BOOT_IMAGE 590 if (format == IMAGE_FORMAT_ANDROID) { 591 struct andr_img_hdr *hdr = img; 592 ulong offset; 593 594 dev_desc = rockchip_get_bootdev(); 595 if (!dev_desc) 596 return -ENODEV; 597 598 offset = hdr->page_size + ALIGN(hdr->kernel_size, hdr->page_size) + 599 ALIGN(hdr->ramdisk_size, hdr->page_size); 600 ret = resource_create_ram_list(dev_desc, (void *)hdr + offset); 601 if (ret) 602 return ret; 603 604 return rockchip_read_dtb_file((void *)fdt); 605 } 606 #endif 607 #if IMAGE_ENABLE_FIT 608 if (format == IMAGE_FORMAT_FIT) { 609 const void *data; 610 size_t size; 611 int noffset; 612 613 noffset = fdt_path_offset(img, "/images/resource"); 614 if (noffset < 0) 615 return noffset; 616 617 ret = fit_image_get_data(img, noffset, &data, &size); 618 if (ret < 0) 619 return ret; 620 621 dev_desc = rockchip_get_bootdev(); 622 if (!dev_desc) 623 return -ENODEV; 624 625 ret = resource_create_ram_list(dev_desc, (void *)data); 626 if (ret) { 627 printf("resource_create_ram_list fail, ret=%d\n", ret); 628 return ret; 629 } 630 631 return rockchip_read_dtb_file((void *)fdt); 632 } 633 #endif 634 635 return -EINVAL; 636 } 637 638 int resource_traverse_init_list(void) 639 { 640 if (!resource_is_empty()) 641 return 0; 642 643 #ifdef CONFIG_ROCKCHIP_FIT_IMAGE 644 if (!fit_image_init_resource()) 645 return 0; 646 #endif 647 #ifdef CONFIG_ROCKCHIP_UIMAGE 648 if (!uimage_init_resource()) 649 return 0; 650 #endif 651 /* Android image is default supported within resource core */ 652 653 return 0; 654 } 655 656 static int do_dump_resource(cmd_tbl_t *cmdtp, int flag, 657 int argc, char *const argv[]) 658 { 659 struct resource_file *file; 660 struct list_head *node; 661 662 printf("Resources:\n"); 663 list_for_each(node, &entrys_head) { 664 file = list_entry(node, struct resource_file, link); 665 printf(" %s: 0x%08x(sector), 0x%08x(bytes)\n", 666 file->name, file->rsce_base + file->f_offset, file->f_size); 667 } 668 669 #ifdef CONFIG_ROCKCHIP_HWID_DTB 670 printf("DTBs:\n"); 671 list_for_each(node, &entrys_dtbs_head) { 672 file = list_entry(node, struct resource_file, dtbs); 673 printf(" %s: 0x%08x(sector),0x%08x(bytes)\n", 674 file->name, file->rsce_base + file->f_offset, file->f_size); 675 } 676 #endif 677 return 0; 678 } 679 680 U_BOOT_CMD( 681 dump_resource, 1, 1, do_dump_resource, 682 "dump resource list", 683 "" 684 ); 685 686