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