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/uimage.h> 15 #include <asm/arch/fit.h> 16 17 DECLARE_GLOBAL_DATA_PTR; 18 19 #define PART_RESOURCE "resource" 20 #define RESOURCE_MAGIC "RSCE" 21 #define RESOURCE_MAGIC_SIZE 4 22 #define ENTRY_TAG "ENTR" 23 #define ENTRY_TAG_SIZE 4 24 #define MAX_FILE_NAME_LEN 220 25 #define MAX_HASH_LEN 32 26 #define DEFAULT_DTB_FILE "rk-kernel.dtb" 27 28 /* 29 * resource image structure 30 * ---------------------------------------------- 31 * | | 32 * | header (1 block) | 33 * | | 34 * ---------------------------------------------| 35 * | | | 36 * | entry0 (1 block) | | 37 * | | | 38 * ------------------------ | 39 * | | | 40 * | entry1 (1 block) | contents (n blocks) | 41 * | | | 42 * ------------------------ | 43 * | ...... | | 44 * ------------------------ | 45 * | | | 46 * | entryn (1 block) | | 47 * | | | 48 * ---------------------------------------------- 49 * | | 50 * | file0 (x blocks) | 51 * | | 52 * ---------------------------------------------- 53 * | | 54 * | file1 (y blocks) | 55 * | | 56 * ---------------------------------------------- 57 * | ...... | 58 * |--------------------------------------------- 59 * | | 60 * | filen (z blocks) | 61 * | | 62 * ---------------------------------------------- 63 */ 64 65 /** 66 * struct resource_img_hdr 67 * 68 * @magic: should be "RSCE" 69 * @version: resource image version, current is 0 70 * @c_version: content version, current is 0 71 * @blks: the size of the header ( 1 block = 512 bytes) 72 * @c_offset: contents offset(by block) in the image 73 * @e_blks: the size(by block) of the entry in the contents 74 * @e_num: numbers of the entrys. 75 */ 76 struct resource_img_hdr { 77 char magic[4]; 78 uint16_t version; 79 uint16_t c_version; 80 uint8_t blks; 81 uint8_t c_offset; 82 uint8_t e_blks; 83 uint32_t e_nums; 84 }; 85 86 struct resource_entry { 87 char tag[4]; 88 char name[MAX_FILE_NAME_LEN]; 89 char hash[MAX_HASH_LEN]; 90 uint32_t hash_size; 91 uint32_t blk_offset; 92 uint32_t size; /* in byte */ 93 }; 94 95 LIST_HEAD(entry_head); 96 97 static int resource_check_header(struct resource_img_hdr *hdr) 98 { 99 return memcmp(RESOURCE_MAGIC, hdr->magic, RESOURCE_MAGIC_SIZE); 100 } 101 102 static void resource_dump(struct resource_file *f) 103 { 104 printf("%s\n", f->name); 105 printf(" blk_start: 0x%08lx\n", (ulong)f->blk_start); 106 printf(" blk_offset: 0x%08lx\n", (ulong)f->blk_offset); 107 printf(" size: 0x%08x\n", f->size); 108 printf(" in_ram: %d\n", f->in_ram); 109 printf(" hash_size: %d\n\n", f->hash_size); 110 } 111 112 static int resource_add_file(const char *name, u32 size, 113 u32 blk_start, u32 blk_offset, 114 char *hash, u32 hash_size, 115 bool in_ram) 116 { 117 struct resource_file *f; 118 struct list_head *node; 119 bool _new = true; 120 121 /* old one ? */ 122 list_for_each(node, &entry_head) { 123 f = list_entry(node, struct resource_file, link); 124 if (!strcmp(f->name, name)) { 125 _new = false; 126 break; 127 } 128 } 129 130 if (_new) { 131 f = calloc(1, sizeof(*f)); 132 if (!f) 133 return -ENOMEM; 134 135 list_add_tail(&f->link, &entry_head); 136 } 137 138 strcpy(f->name, name); 139 f->size = size; 140 f->in_ram = in_ram; 141 f->blk_start = blk_start; 142 f->blk_offset = blk_offset; 143 f->hash_size = hash_size; 144 memcpy(f->hash, hash, hash_size); 145 #ifdef DEBUG 146 resource_dump(f); 147 #endif 148 return 0; 149 } 150 151 #ifdef CONFIG_ANDROID_BOOT_IMAGE 152 /* 153 * Add logo.bmp and logo_kernel.bmp from "logo" parititon 154 * 155 * Provide a "logo" partition for user to store logo.bmp and 156 * logo_kernel.bmp, so that the user can update them from 157 * kernel or user-space dynamically. 158 * 159 * "logo" partition layout, do not change order: 160 * 161 * |----------------------| 0x00 162 * | raw logo.bmp | 163 * |----------------------| -> 512-byte aligned 164 * | raw logo_kernel.bmp | 165 * |----------------------| 166 * 167 * N: the sector count of logo.bmp 168 * 169 * How to generate: 170 * cat logo.bmp > logo.img && truncate -s %512 logo.img && cat logo_kernel.bmp >> logo.img 171 */ 172 static int resource_setup_logo_bmp(struct blk_desc *desc) 173 { 174 struct bmp_header *header; 175 const char *name[] = { "logo.bmp", "logo_kernel.bmp" }; 176 disk_partition_t part; 177 u32 blk_offset = 0; 178 u32 filesz; 179 int ret, i; 180 181 if (part_get_info_by_name(desc, PART_LOGO, &part) < 0) 182 return 0; 183 184 header = memalign(ARCH_DMA_MINALIGN, desc->blksz); 185 if (!header) 186 return -ENOMEM; 187 188 for (i = 0; i < ARRAY_SIZE(name); i++) { 189 if (blk_dread(desc, part.start + blk_offset, 1, header) != 1) { 190 ret = -EIO; 191 break; 192 } 193 194 if (header->signature[0] != 'B' || header->signature[1] != 'M') { 195 ret = -EINVAL; 196 break; 197 } 198 199 filesz = get_unaligned_le32(&header->file_size); 200 ret = resource_add_file(name[i], filesz, part.start, blk_offset, 201 NULL, 0, false); 202 if (ret) 203 break; 204 205 /* move to next file */ 206 blk_offset += DIV_ROUND_UP(filesz, desc->blksz); 207 208 printf("LOGO: %s\n", name[i]); 209 210 } 211 212 free(header); 213 214 return ret; 215 } 216 #endif 217 218 static int resource_setup_list(struct blk_desc *desc, ulong blk_start, 219 void *resc_hdr, bool in_ram) 220 { 221 struct resource_img_hdr *hdr = resc_hdr; 222 struct resource_entry *et; 223 u32 i, stride; 224 void *pos; 225 226 pos = (void *)hdr + hdr->c_offset * desc->blksz; 227 stride = hdr->e_blks * desc->blksz; 228 229 for (i = 0; i < hdr->e_nums; i++) { 230 et = pos + (i * stride); 231 if (memcmp(et->tag, ENTRY_TAG, ENTRY_TAG_SIZE)) 232 continue; 233 234 resource_add_file(et->name, et->size, 235 blk_start, et->blk_offset, 236 et->hash, et->hash_size, in_ram); 237 } 238 #ifdef CONFIG_ANDROID_BOOT_IMAGE 239 resource_setup_logo_bmp(desc); 240 #endif 241 return 0; 242 } 243 244 int resource_setup_ram_list(struct blk_desc *desc, void *hdr) 245 { 246 if (!desc) 247 return -ENODEV; 248 249 if (resource_check_header(hdr)) { 250 printf("RESC: invalid\n"); 251 return -EINVAL; 252 } 253 254 /* @blk_start: set as 'hdr' point addr, to be used in byte */ 255 return resource_setup_list(desc, (ulong)hdr, hdr, true); 256 } 257 258 #ifdef CONFIG_ANDROID_BOOT_IMAGE 259 static int resource_setup_blk_list(struct blk_desc *desc, ulong blk_start) 260 { 261 struct resource_img_hdr *hdr; 262 int blk_cnt; 263 int ret = 0; 264 void *buf; 265 266 hdr = memalign(ARCH_DMA_MINALIGN, desc->blksz); 267 if (!hdr) 268 return -ENOMEM; 269 270 if (blk_dread(desc, blk_start, 1, hdr) != 1) { 271 ret = -EIO; 272 goto out; 273 } 274 275 if (resource_check_header(hdr)) { 276 printf("RESC: invalid\n"); 277 if (fdt_check_header(hdr)) { 278 ret = -EINVAL; 279 goto out; 280 } else { 281 /* this is a dtb file */ 282 printf("RESC: this is dtb\n"); 283 ret = resource_add_file(DEFAULT_DTB_FILE, 284 fdt_totalsize(hdr), 285 blk_start, 0, NULL, 0, false); 286 goto out; 287 } 288 } 289 290 blk_cnt = hdr->e_blks * hdr->e_nums; 291 hdr = realloc(hdr, (1 + blk_cnt) * desc->blksz); 292 if (!hdr) { 293 ret = -ENOMEM; 294 goto out; 295 } 296 297 buf = (void *)hdr + desc->blksz; 298 if (blk_dread(desc, blk_start + hdr->c_offset, blk_cnt, buf) != blk_cnt) { 299 ret = -EIO; 300 goto out; 301 } 302 303 resource_setup_list(desc, blk_start, hdr, false); 304 out: 305 free(hdr); 306 307 return ret; 308 } 309 310 static int resource_init(struct blk_desc *desc, 311 disk_partition_t *part, 312 ulong blk_offset) 313 { 314 printf("RESC: '%s', blk@0x%08lx\n", part->name, part->start + blk_offset); 315 316 #ifdef CONFIG_ANDROID_AVB 317 char hdr[512]; 318 ulong resc_buf = 0; 319 int ret; 320 321 if (blk_dread(desc, part->start, 1, hdr) != 1) 322 return -EIO; 323 324 /* only handle android boot/recovery.img and resource.img, ignore fit */ 325 if (!android_image_check_header((void *)hdr) || 326 !resource_check_header((void *)hdr)) { 327 ret = android_image_verify_resource((const char *)part->name, &resc_buf); 328 if (ret) { 329 printf("RESC: '%s', avb verify fail: %d\n", part->name, ret); 330 return ret; 331 } 332 333 /* 334 * unlock=0: resc_buf is valid and file was already full load in ram. 335 * unlock=1: resc_buf is 0. 336 */ 337 if (resc_buf && !resource_check_header((void *)resc_buf)) 338 return resource_setup_ram_list(desc, (void *)resc_buf); 339 } 340 #endif 341 342 return resource_setup_blk_list(desc, part->start + blk_offset); 343 } 344 345 static int resource_default(struct blk_desc *desc, 346 disk_partition_t *out_part, 347 ulong *out_blk_offset) 348 { 349 disk_partition_t part; 350 351 if (part_get_info_by_name(desc, PART_RESOURCE, &part) < 0) 352 return -ENODEV; 353 354 *out_part = part; 355 *out_blk_offset = 0; 356 357 return 0; 358 } 359 #endif 360 361 static int resource_scan(void) 362 { 363 struct blk_desc *desc = rockchip_get_bootdev(); 364 __maybe_unused int ret; 365 366 if (!desc) { 367 printf("RESC: No bootdev\n"); 368 return -ENODEV; 369 } 370 371 if (!list_empty(&entry_head)) 372 return 0; 373 374 #ifdef CONFIG_ROCKCHIP_FIT_IMAGE 375 ret = fit_image_init_resource(desc); 376 if (!ret || ret != -EAGAIN) 377 return ret; 378 #endif 379 #ifdef CONFIG_ROCKCHIP_UIMAGE 380 ret = uimage_init_resource(desc); 381 if (!ret || ret != -EAGAIN) 382 return ret; 383 #endif 384 #ifdef CONFIG_ANDROID_BOOT_IMAGE 385 disk_partition_t part; 386 ulong blk_offset; 387 char hdr[512]; 388 char name[32]; 389 390 /* partition priority: boot/recovery > resource */ 391 if (!android_image_init_resource(desc, &part, &blk_offset)) { 392 if (blk_dread(desc, part.start + blk_offset, 1, hdr) != 1) 393 return -EIO; 394 395 if (resource_check_header((void *)hdr)) { 396 strcpy(name, (char *)part.name); 397 if (resource_default(desc, &part, &blk_offset)) 398 return -ENOENT; 399 400 printf("RESC: '%s' -> '%s'\n", name, part.name); 401 } 402 } else { 403 if (resource_default(desc, &part, &blk_offset)) 404 return -ENOENT; 405 } 406 407 /* now, 'part' can be boot/recovery/resource */ 408 return resource_init(desc, &part, blk_offset); 409 #endif 410 return -ENOENT; 411 } 412 413 static struct resource_file *resource_get_file(const char *name) 414 { 415 struct resource_file *f; 416 struct list_head *node; 417 418 if (resource_scan()) 419 return NULL; 420 421 list_for_each(node, &entry_head) { 422 f = list_entry(node, struct resource_file, link); 423 if (!strcmp(f->name, name)) 424 return f; 425 } 426 427 return NULL; 428 } 429 430 int rockchip_read_resource_file(void *buf, const char *name, int blk_offset, int len) 431 { 432 struct blk_desc *desc = rockchip_get_bootdev(); 433 struct resource_file *f; 434 int blk_cnt; 435 ulong pos; 436 437 if (!desc) 438 return -ENODEV; 439 440 f = resource_get_file(name); 441 if (!f) { 442 printf("No resource file: %s\n", name); 443 return -ENOENT; 444 } 445 446 if (len <= 0 || len > f->size) 447 len = f->size; 448 449 if (f->in_ram) { 450 pos = f->blk_start + (f->blk_offset + blk_offset) * desc->blksz; 451 memcpy(buf, (char *)pos, len); 452 } else { 453 blk_cnt = DIV_ROUND_UP(len, desc->blksz); 454 if (blk_dread(desc, 455 f->blk_start + f->blk_offset + blk_offset, 456 blk_cnt, buf) != blk_cnt) 457 len = -EIO; 458 } 459 460 return len; 461 } 462 463 extern struct resource_file *resource_read_hwid_dtb(void); 464 465 int rockchip_read_resource_dtb(void *fdt_addr, char **hash, int *hash_size) 466 { 467 struct resource_file *f = NULL; 468 int ret; 469 470 #ifdef CONFIG_ROCKCHIP_HWID_DTB 471 if (resource_scan()) 472 return -ENOENT; 473 474 f = resource_read_hwid_dtb(); 475 #endif 476 /* If no dtb match hardware id(GPIO/ADC), use the default */ 477 if (!f) 478 f = resource_get_file(DEFAULT_DTB_FILE); 479 480 if (!f) 481 return -ENODEV; 482 483 ret = rockchip_read_resource_file(fdt_addr, f->name, 0, 0); 484 if (ret < 0) 485 return ret; 486 487 if (fdt_check_header(fdt_addr)) 488 return -EBADF; 489 490 *hash = f->hash; 491 *hash_size = f->hash_size; 492 493 printf("DTB: %s\n", f->name); 494 495 return 0; 496 } 497 498 static int do_dump_resource(cmd_tbl_t *cmdtp, int flag, 499 int argc, char *const argv[]) 500 { 501 struct resource_file *f; 502 struct list_head *node; 503 504 list_for_each(node, &entry_head) { 505 f = list_entry(node, struct resource_file, link); 506 resource_dump(f); 507 } 508 509 return 0; 510 } 511 512 U_BOOT_CMD( 513 dump_resource, 1, 1, do_dump_resource, 514 "dump resource files", 515 "" 516 ); 517 518