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