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