imx8mimage.c 15 KB

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