1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright 2018 NXP 4 * 5 * Peng Fan <peng.fan@nxp.com> 6 */ 7 8 9 #include "imagetool.h" 10 #include <image.h> 11 #include "imximage.h" 12 #include "compiler.h" 13 14 static uint32_t ap_start_addr, sld_start_addr, sld_src_off; 15 static char *ap_img, *sld_img, *signed_hdmi; 16 static imx_header_v3_t imx_header[2]; /* At most there are 3 IVT headers */ 17 static uint32_t rom_image_offset; 18 static uint32_t sector_size = 0x200; 19 static uint32_t image_off; 20 static uint32_t sld_header_off; 21 static uint32_t ivt_offset; 22 static uint32_t using_fit; 23 24 #define CSF_SIZE 0x2000 25 #define HDMI_IVT_ID 0 26 #define IMAGE_IVT_ID 1 27 28 #define HDMI_FW_SIZE 0x17000 /* Use Last 0x1000 for IVT and CSF */ 29 #define ALIGN_SIZE 0x1000 30 #define ALIGN(x,a) __ALIGN_MASK((x), (__typeof__(x))(a) - 1, a) 31 #define __ALIGN_MASK(x,mask,mask2) (((x) + (mask)) / (mask2) * (mask2)) 32 33 static uint32_t get_cfg_value(char *token, char *name, int linenr) 34 { 35 char *endptr; 36 uint32_t value; 37 38 errno = 0; 39 value = strtoul(token, &endptr, 16); 40 if (errno || token == endptr) { 41 fprintf(stderr, "Error: %s[%d] - Invalid hex data(%s)\n", 42 name, linenr, token); 43 exit(EXIT_FAILURE); 44 } 45 return value; 46 } 47 48 int imx8mimage_check_params(struct image_tool_params *params) 49 { 50 return 0; 51 } 52 53 static void imx8mimage_set_header(void *ptr, struct stat *sbuf, int ifd, 54 struct image_tool_params *params) 55 { 56 } 57 58 static void imx8mimage_print_header(const void *ptr) 59 { 60 } 61 62 static int imx8mimage_check_image_types(uint8_t type) 63 { 64 return (type == IH_TYPE_IMX8MIMAGE) ? EXIT_SUCCESS : EXIT_FAILURE; 65 } 66 67 static table_entry_t imx8mimage_cmds[] = { 68 {CMD_BOOT_FROM, "BOOT_FROM", "boot command", }, 69 {CMD_FIT, "FIT", "fit image", }, 70 {CMD_SIGNED_HDMI, "SIGNED_HDMI", "signed hdmi image", }, 71 {CMD_LOADER, "LOADER", "loader image", }, 72 {CMD_SECOND_LOADER, "SECOND_LOADER", "2nd loader image", }, 73 {CMD_DDR_FW, "DDR_FW", "ddr firmware", }, 74 {-1, "", "", }, 75 }; 76 77 static table_entry_t imx8mimage_ivt_offset[] = { 78 {0x400, "sd", "sd/emmc",}, 79 {0x400, "emmc_fastboot", "emmc fastboot",}, 80 {0x1000, "fspi", "flexspi", }, 81 {-1, "", "Invalid", }, 82 }; 83 84 static void parse_cfg_cmd(int32_t cmd, char *token, char *name, int lineno) 85 { 86 switch (cmd) { 87 case CMD_BOOT_FROM: 88 ivt_offset = get_table_entry_id(imx8mimage_ivt_offset, 89 "imx8mimage ivt offset", 90 token); 91 if (!strncmp(token, "sd", 2)) 92 rom_image_offset = 0x8000; 93 break; 94 case CMD_LOADER: 95 ap_img = token; 96 break; 97 case CMD_SECOND_LOADER: 98 sld_img = token; 99 break; 100 case CMD_SIGNED_HDMI: 101 signed_hdmi = token; 102 case CMD_FIT: 103 using_fit = 1; 104 break; 105 case CMD_DDR_FW: 106 /* Do nothing */ 107 break; 108 } 109 } 110 111 static void parse_cfg_fld(int32_t *cmd, char *token, 112 char *name, int lineno, int fld) 113 { 114 switch (fld) { 115 case CFG_COMMAND: 116 *cmd = get_table_entry_id(imx8mimage_cmds, 117 "imx8mimage commands", token); 118 if (*cmd < 0) { 119 fprintf(stderr, "Error: %s[%d] - Invalid command" "(%s)\n", 120 name, lineno, token); 121 exit(EXIT_FAILURE); 122 } 123 break; 124 case CFG_REG_SIZE: 125 parse_cfg_cmd(*cmd, token, name, lineno); 126 break; 127 case CFG_REG_ADDRESS: 128 switch (*cmd) { 129 case CMD_LOADER: 130 ap_start_addr = get_cfg_value(token, name, lineno); 131 break; 132 case CMD_SECOND_LOADER: 133 sld_start_addr = get_cfg_value(token, name, lineno); 134 break; 135 } 136 break; 137 case CFG_REG_VALUE: 138 switch (*cmd) { 139 case CMD_SECOND_LOADER: 140 sld_src_off = get_cfg_value(token, name, lineno); 141 break; 142 } 143 default: 144 break; 145 } 146 } 147 148 static uint32_t parse_cfg_file(char *name) 149 { 150 FILE *fd = NULL; 151 char *line = NULL; 152 char *token, *saveptr1, *saveptr2; 153 int lineno = 0; 154 int fld; 155 size_t len; 156 int32_t cmd; 157 158 fd = fopen(name, "r"); 159 if (fd == 0) { 160 fprintf(stderr, "Error: %s - Can't open cfg file\n", name); 161 exit(EXIT_FAILURE); 162 } 163 164 /* 165 * Very simple parsing, line starting with # are comments 166 * and are dropped 167 */ 168 while ((getline(&line, &len, fd)) > 0) { 169 lineno++; 170 171 token = strtok_r(line, "\r\n", &saveptr1); 172 if (!token) 173 continue; 174 175 /* Check inside the single line */ 176 for (fld = CFG_COMMAND, cmd = CFG_INVALID, 177 line = token; ; line = NULL, fld++) { 178 token = strtok_r(line, " \t", &saveptr2); 179 if (!token) 180 break; 181 182 /* Drop all text starting with '#' as comments */ 183 if (token[0] == '#') 184 break; 185 186 parse_cfg_fld(&cmd, token, name, lineno, fld); 187 } 188 } 189 190 return 0; 191 } 192 193 static void fill_zero(int ifd, int size, int offset) 194 { 195 int fill_size; 196 uint8_t zeros[4096]; 197 int ret; 198 199 memset(zeros, 0, sizeof(zeros)); 200 201 ret = lseek(ifd, offset, SEEK_SET); 202 if (ret < 0) { 203 fprintf(stderr, "%s seek: %s\n", __func__, strerror(errno)); 204 exit(EXIT_FAILURE); 205 } 206 207 while (size) { 208 if (size > 4096) 209 fill_size = 4096; 210 else 211 fill_size = size; 212 213 if (write(ifd, (char *)&zeros, fill_size) != fill_size) { 214 fprintf(stderr, "Write error: %s\n", 215 strerror(errno)); 216 exit(EXIT_FAILURE); 217 } 218 219 size -= fill_size; 220 }; 221 } 222 223 static void copy_file(int ifd, const char *datafile, int pad, int offset, 224 int datafile_offset) 225 { 226 int dfd; 227 struct stat sbuf; 228 unsigned char *ptr; 229 int tail; 230 int zero = 0; 231 uint8_t zeros[4096]; 232 int size, ret; 233 234 memset(zeros, 0, sizeof(zeros)); 235 236 dfd = open(datafile, O_RDONLY | O_BINARY); 237 if (dfd < 0) { 238 fprintf(stderr, "Can't open %s: %s\n", 239 datafile, strerror(errno)); 240 exit(EXIT_FAILURE); 241 } 242 243 if (fstat(dfd, &sbuf) < 0) { 244 fprintf(stderr, "Can't stat %s: %s\n", 245 datafile, strerror(errno)); 246 exit(EXIT_FAILURE); 247 } 248 249 ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, dfd, 0); 250 if (ptr == MAP_FAILED) { 251 fprintf(stderr, "Can't read %s: %s\n", 252 datafile, strerror(errno)); 253 exit(EXIT_FAILURE); 254 } 255 256 size = sbuf.st_size - datafile_offset; 257 ret = lseek(ifd, offset, SEEK_SET); 258 if (ret < 0) { 259 fprintf(stderr, "lseek ifd fail\n"); 260 exit(EXIT_FAILURE); 261 } 262 263 if (write(ifd, ptr + datafile_offset, size) != size) { 264 fprintf(stderr, "Write error %s\n", 265 strerror(errno)); 266 exit(EXIT_FAILURE); 267 } 268 269 tail = size % 4; 270 pad = pad - size; 271 if (pad == 1 && tail != 0) { 272 if (write(ifd, (char *)&zero, 4 - tail) != 4 - tail) { 273 fprintf(stderr, "Write error on %s\n", 274 strerror(errno)); 275 exit(EXIT_FAILURE); 276 } 277 } else if (pad > 1) { 278 while (pad > 0) { 279 int todo = sizeof(zeros); 280 281 if (todo > pad) 282 todo = pad; 283 if (write(ifd, (char *)&zeros, todo) != todo) { 284 fprintf(stderr, "Write error: %s\n", 285 strerror(errno)); 286 exit(EXIT_FAILURE); 287 } 288 pad -= todo; 289 } 290 } 291 292 munmap((void *)ptr, sbuf.st_size); 293 close(dfd); 294 } 295 296 /* Return this IVT offset in the final output file */ 297 static int generate_ivt_for_fit(int fd, int fit_offset, uint32_t ep, 298 uint32_t *fit_load_addr) 299 { 300 image_header_t image_header; 301 int ret; 302 303 uint32_t fit_size, load_addr; 304 int align_len = 64 - 1; /* 64 is cacheline size */ 305 306 ret = lseek(fd, fit_offset, SEEK_SET); 307 if (ret < 0) { 308 fprintf(stderr, "lseek fd fail for fit\n"); 309 exit(EXIT_FAILURE); 310 } 311 312 if (read(fd, (char *)&image_header, sizeof(image_header_t)) != 313 sizeof(image_header_t)) { 314 fprintf(stderr, "generate_ivt_for_fit read failed: %s\n", 315 strerror(errno)); 316 exit(EXIT_FAILURE); 317 } 318 319 if (be32_to_cpu(image_header.ih_magic) != FDT_MAGIC) { 320 fprintf(stderr, "%s error: not a FIT file\n", __func__); 321 exit(EXIT_FAILURE); 322 } 323 324 fit_size = fdt_totalsize(&image_header); 325 fit_size = (fit_size + 3) & ~3; 326 327 fit_size = ALIGN(fit_size, ALIGN_SIZE); 328 329 ret = lseek(fd, fit_offset + fit_size, SEEK_SET); 330 if (ret < 0) { 331 fprintf(stderr, "lseek fd fail for fit\n"); 332 exit(EXIT_FAILURE); 333 } 334 335 /* 336 * ep is the u-boot entry. SPL loads the FIT before the u-boot 337 * address. 0x2000 is for CSF_SIZE 338 */ 339 load_addr = (ep - (fit_size + CSF_SIZE) - 512 - align_len) & 340 ~align_len; 341 342 flash_header_v2_t ivt_header = { { 0xd1, 0x2000, 0x40 }, 343 load_addr, 0, 0, 0, 344 (load_addr + fit_size), 345 (load_addr + fit_size + 0x20), 346 0 }; 347 348 if (write(fd, &ivt_header, sizeof(flash_header_v2_t)) != 349 sizeof(flash_header_v2_t)) { 350 fprintf(stderr, "IVT writing error on fit image\n"); 351 exit(EXIT_FAILURE); 352 } 353 354 *fit_load_addr = load_addr; 355 356 return fit_offset + fit_size; 357 } 358 359 static void dump_header_v2(imx_header_v3_t *imx_header, int index) 360 { 361 const char *ivt_name[2] = {"HDMI FW", "LOADER IMAGE"}; 362 363 fprintf(stdout, "========= IVT HEADER [%s] =========\n", 364 ivt_name[index]); 365 fprintf(stdout, "header.tag: \t\t0x%x\n", 366 imx_header[index].fhdr.header.tag); 367 fprintf(stdout, "header.length: \t\t0x%x\n", 368 imx_header[index].fhdr.header.length); 369 fprintf(stdout, "header.version: \t0x%x\n", 370 imx_header[index].fhdr.header.version); 371 fprintf(stdout, "entry: \t\t\t0x%x\n", 372 imx_header[index].fhdr.entry); 373 fprintf(stdout, "reserved1: \t\t0x%x\n", 374 imx_header[index].fhdr.reserved1); 375 fprintf(stdout, "dcd_ptr: \t\t0x%x\n", 376 imx_header[index].fhdr.dcd_ptr); 377 fprintf(stdout, "boot_data_ptr: \t\t0x%x\n", 378 imx_header[index].fhdr.boot_data_ptr); 379 fprintf(stdout, "self: \t\t\t0x%x\n", 380 imx_header[index].fhdr.self); 381 fprintf(stdout, "csf: \t\t\t0x%x\n", 382 imx_header[index].fhdr.csf); 383 fprintf(stdout, "reserved2: \t\t0x%x\n", 384 imx_header[index].fhdr.reserved2); 385 386 fprintf(stdout, "boot_data.start: \t0x%x\n", 387 imx_header[index].boot_data.start); 388 fprintf(stdout, "boot_data.size: \t0x%x\n", 389 imx_header[index].boot_data.size); 390 fprintf(stdout, "boot_data.plugin: \t0x%x\n", 391 imx_header[index].boot_data.plugin); 392 } 393 394 void build_image(int ofd) 395 { 396 int file_off, header_hdmi_off = 0, header_image_off; 397 int hdmi_fd, ap_fd, sld_fd; 398 uint32_t sld_load_addr = 0; 399 uint32_t csf_off, sld_csf_off = 0; 400 int ret; 401 struct stat sbuf; 402 403 if (!ap_img) { 404 fprintf(stderr, "No LOADER image specificed\n"); 405 exit(EXIT_FAILURE); 406 } 407 408 file_off = 0; 409 410 if (signed_hdmi) { 411 header_hdmi_off = file_off + ivt_offset; 412 413 hdmi_fd = open(signed_hdmi, O_RDONLY | O_BINARY); 414 if (hdmi_fd < 0) { 415 fprintf(stderr, "%s: Can't open: %s\n", 416 signed_hdmi, strerror(errno)); 417 exit(EXIT_FAILURE); 418 } 419 420 if (fstat(hdmi_fd, &sbuf) < 0) { 421 fprintf(stderr, "%s: Can't stat: %s\n", 422 signed_hdmi, strerror(errno)); 423 exit(EXIT_FAILURE); 424 } 425 close(hdmi_fd); 426 427 /* 428 * Aligned to 104KB = 92KB FW image + 0x8000 429 * (IVT and alignment) + 0x4000 (second IVT + CSF) 430 */ 431 file_off += ALIGN(sbuf.st_size, 432 HDMI_FW_SIZE + 0x2000 + 0x1000); 433 } 434 435 header_image_off = file_off + ivt_offset; 436 437 ap_fd = open(ap_img, O_RDONLY | O_BINARY); 438 if (ap_fd < 0) { 439 fprintf(stderr, "%s: Can't open: %s\n", 440 ap_img, strerror(errno)); 441 exit(EXIT_FAILURE); 442 } 443 if (fstat(ap_fd, &sbuf) < 0) { 444 fprintf(stderr, "%s: Can't stat: %s\n", 445 ap_img, strerror(errno)); 446 exit(EXIT_FAILURE); 447 } 448 close(ap_fd); 449 450 imx_header[IMAGE_IVT_ID].fhdr.header.tag = IVT_HEADER_TAG; /* 0xD1 */ 451 imx_header[IMAGE_IVT_ID].fhdr.header.length = 452 cpu_to_be16(sizeof(flash_header_v2_t)); 453 imx_header[IMAGE_IVT_ID].fhdr.header.version = IVT_VERSION_V3; /* 0x41 */ 454 imx_header[IMAGE_IVT_ID].fhdr.entry = ap_start_addr; 455 imx_header[IMAGE_IVT_ID].fhdr.self = ap_start_addr - 456 sizeof(imx_header_v3_t); 457 imx_header[IMAGE_IVT_ID].fhdr.dcd_ptr = 0; 458 imx_header[IMAGE_IVT_ID].fhdr.boot_data_ptr = 459 imx_header[IMAGE_IVT_ID].fhdr.self + 460 offsetof(imx_header_v3_t, boot_data); 461 imx_header[IMAGE_IVT_ID].boot_data.start = 462 imx_header[IMAGE_IVT_ID].fhdr.self - ivt_offset; 463 imx_header[IMAGE_IVT_ID].boot_data.size = 464 ALIGN(sbuf.st_size + sizeof(imx_header_v3_t) + ivt_offset, 465 sector_size); 466 467 image_off = header_image_off + sizeof(imx_header_v3_t); 468 file_off += imx_header[IMAGE_IVT_ID].boot_data.size; 469 470 imx_header[IMAGE_IVT_ID].boot_data.plugin = 0; 471 imx_header[IMAGE_IVT_ID].fhdr.csf = 472 imx_header[IMAGE_IVT_ID].boot_data.start + 473 imx_header[IMAGE_IVT_ID].boot_data.size; 474 475 imx_header[IMAGE_IVT_ID].boot_data.size += CSF_SIZE; /* 8K region dummy CSF */ 476 477 csf_off = file_off; 478 file_off += CSF_SIZE; 479 480 /* Second boot loader image */ 481 if (sld_img) { 482 if (!using_fit) { 483 fprintf(stderr, "Not support no fit\n"); 484 exit(EXIT_FAILURE); 485 } else { 486 sld_header_off = sld_src_off - rom_image_offset; 487 /* 488 * Record the second bootloader relative offset in 489 * image's IVT reserved1 490 */ 491 imx_header[IMAGE_IVT_ID].fhdr.reserved1 = 492 sld_header_off - header_image_off; 493 sld_fd = open(sld_img, O_RDONLY | O_BINARY); 494 if (sld_fd < 0) { 495 fprintf(stderr, "%s: Can't open: %s\n", 496 sld_img, strerror(errno)); 497 exit(EXIT_FAILURE); 498 } 499 500 if (fstat(sld_fd, &sbuf) < 0) { 501 fprintf(stderr, "%s: Can't stat: %s\n", 502 sld_img, strerror(errno)); 503 exit(EXIT_FAILURE); 504 } 505 506 close(sld_fd); 507 508 file_off = sld_header_off; 509 file_off += sbuf.st_size + sizeof(image_header_t); 510 } 511 } 512 513 if (signed_hdmi) { 514 header_hdmi_off -= ivt_offset; 515 ret = lseek(ofd, header_hdmi_off, SEEK_SET); 516 if (ret < 0) { 517 fprintf(stderr, "lseek ofd fail for hdmi\n"); 518 exit(EXIT_FAILURE); 519 } 520 521 /* The signed HDMI FW has 0x400 IVT offset, need remove it */ 522 copy_file(ofd, signed_hdmi, 0, header_hdmi_off, 0x400); 523 } 524 525 /* Main Image */ 526 header_image_off -= ivt_offset; 527 image_off -= ivt_offset; 528 ret = lseek(ofd, header_image_off, SEEK_SET); 529 if (ret < 0) { 530 fprintf(stderr, "lseek ofd fail\n"); 531 exit(EXIT_FAILURE); 532 } 533 534 /* Write image header */ 535 if (write(ofd, &imx_header[IMAGE_IVT_ID], sizeof(imx_header_v3_t)) != 536 sizeof(imx_header_v3_t)) { 537 fprintf(stderr, "error writing image hdr\n"); 538 exit(1); 539 } 540 541 copy_file(ofd, ap_img, 0, image_off, 0); 542 543 csf_off -= ivt_offset; 544 fill_zero(ofd, CSF_SIZE, csf_off); 545 546 if (sld_img) { 547 sld_header_off -= ivt_offset; 548 ret = lseek(ofd, sld_header_off, SEEK_SET); 549 if (ret < 0) { 550 fprintf(stderr, "lseek ofd fail for sld_img\n"); 551 exit(EXIT_FAILURE); 552 } 553 554 /* Write image header */ 555 if (!using_fit) { 556 /* TODO */ 557 } else { 558 copy_file(ofd, sld_img, 0, sld_header_off, 0); 559 sld_csf_off = 560 generate_ivt_for_fit(ofd, sld_header_off, 561 sld_start_addr, 562 &sld_load_addr) + 0x20; 563 } 564 } 565 566 if (!signed_hdmi) 567 dump_header_v2(imx_header, 0); 568 dump_header_v2(imx_header, 1); 569 570 fprintf(stdout, "========= OFFSET dump ========="); 571 if (signed_hdmi) { 572 fprintf(stdout, "\nSIGNED HDMI FW:\n"); 573 fprintf(stdout, " header_hdmi_off \t0x%x\n", 574 header_hdmi_off); 575 } 576 577 fprintf(stdout, "\nLoader IMAGE:\n"); 578 fprintf(stdout, " header_image_off \t0x%x\n image_off \t\t0x%x\n csf_off \t\t0x%x\n", 579 header_image_off, image_off, csf_off); 580 fprintf(stdout, " spl hab block: \t0x%x 0x%x 0x%x\n", 581 imx_header[IMAGE_IVT_ID].fhdr.self, header_image_off, 582 csf_off - header_image_off); 583 584 fprintf(stdout, "\nSecond Loader IMAGE:\n"); 585 fprintf(stdout, " sld_header_off \t0x%x\n", 586 sld_header_off); 587 fprintf(stdout, " sld_csf_off \t\t0x%x\n", 588 sld_csf_off); 589 fprintf(stdout, " sld hab block: \t0x%x 0x%x 0x%x\n", 590 sld_load_addr, sld_header_off, sld_csf_off - sld_header_off); 591 } 592 593 int imx8mimage_copy_image(int outfd, struct image_tool_params *mparams) 594 { 595 /* 596 * SECO FW is a container image, this is to calculate the 597 * 2nd container offset. 598 */ 599 fprintf(stdout, "parsing %s\n", mparams->imagename); 600 parse_cfg_file(mparams->imagename); 601 602 build_image(outfd); 603 604 return 0; 605 } 606 607 /* 608 * imx8mimage parameters 609 */ 610 U_BOOT_IMAGE_TYPE( 611 imx8mimage, 612 "NXP i.MX8M Boot Image support", 613 0, 614 NULL, 615 imx8mimage_check_params, 616 NULL, 617 imx8mimage_print_header, 618 imx8mimage_set_header, 619 NULL, 620 imx8mimage_check_image_types, 621 NULL, 622 NULL 623 ); 624