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