2 * Image manipulator for Marvell SoCs
3 * supports Kirkwood, Dove, Armada 370, and Armada XP
5 * (C) Copyright 2013 Thomas Petazzoni
6 * <thomas.petazzoni@free-electrons.com>
8 * SPDX-License-Identifier: GPL-2.0+
10 * Not implemented: support for the register headers and secure
11 * headers in v1 images
14 #include "imagetool.h"
20 #define ALIGN_SUP(x, a) (((x) + (a - 1)) & ~(a - 1))
22 /* Structure of the main header, version 0 (Kirkwood, Dove) */
24 uint8_t blockid
; /*0 */
25 uint8_t nandeccmode
; /*1 */
26 uint16_t nandpagesize
; /*2-3 */
27 uint32_t blocksize
; /*4-7 */
28 uint32_t rsvd1
; /*8-11 */
29 uint32_t srcaddr
; /*12-15 */
30 uint32_t destaddr
; /*16-19 */
31 uint32_t execaddr
; /*20-23 */
32 uint8_t satapiomode
; /*24 */
33 uint8_t rsvd3
; /*25 */
34 uint16_t ddrinitdelay
; /*26-27 */
35 uint16_t rsvd2
; /*28-29 */
37 uint8_t checksum
; /*31 */
40 struct ext_hdr_v0_reg
{
45 #define EXT_HDR_V0_REG_COUNT ((0x1dc - 0x20) / sizeof(struct ext_hdr_v0_reg))
49 uint8_t reserved
[0x20 - sizeof(uint32_t)];
50 struct ext_hdr_v0_reg rcfg
[EXT_HDR_V0_REG_COUNT
];
55 /* Structure of the main header, version 1 (Armada 370, Armada XP) */
57 uint8_t blockid
; /* 0 */
58 uint8_t reserved1
; /* 1 */
59 uint16_t reserved2
; /* 2-3 */
60 uint32_t blocksize
; /* 4-7 */
61 uint8_t version
; /* 8 */
62 uint8_t headersz_msb
; /* 9 */
63 uint16_t headersz_lsb
; /* A-B */
64 uint32_t srcaddr
; /* C-F */
65 uint32_t destaddr
; /* 10-13 */
66 uint32_t execaddr
; /* 14-17 */
67 uint8_t reserved3
; /* 18 */
68 uint8_t nandblocksize
; /* 19 */
69 uint8_t nandbadblklocation
; /* 1A */
70 uint8_t reserved4
; /* 1B */
71 uint16_t reserved5
; /* 1C-1D */
73 uint8_t checksum
; /* 1F */
77 * Header for the optional headers, version 1 (Armada 370, Armada XP)
82 uint16_t headersz_lsb
;
87 * Various values for the opt_hdr_v1->headertype field, describing the
88 * different types of optional headers. The "secure" header contains
89 * informations related to secure boot (encryption keys, etc.). The
90 * "binary" header contains ARM binary code to be executed prior to
91 * executing the main payload (usually the bootloader). This is
92 * typically used to execute DDR3 training code. The "register" header
93 * allows to describe a set of (address, value) tuples that are
94 * generally used to configure the DRAM controller.
96 #define OPT_HDR_V1_SECURE_TYPE 0x1
97 #define OPT_HDR_V1_BINARY_TYPE 0x2
98 #define OPT_HDR_V1_REGISTER_TYPE 0x3
100 #define KWBHEADER_V1_SIZE(hdr) \
101 (((hdr)->headersz_msb << 16) | (hdr)->headersz_lsb)
103 static struct image_cfg_element
*image_cfg
;
111 struct boot_mode boot_modes
[] = {
121 struct nand_ecc_mode
{
126 struct nand_ecc_mode nand_ecc_modes
[] = {
130 { 0x03, "disabled" },
134 /* Used to identify an undefined execution or destination address */
135 #define ADDR_INVALID ((uint32_t)-1)
137 #define BINARY_MAX_ARGS 8
139 /* In-memory representation of a line of the configuration file */
140 struct image_cfg_element
{
142 IMAGE_CFG_VERSION
= 0x1,
146 IMAGE_CFG_NAND_BLKSZ
,
147 IMAGE_CFG_NAND_BADBLK_LOCATION
,
148 IMAGE_CFG_NAND_ECC_MODE
,
149 IMAGE_CFG_NAND_PAGESZ
,
155 unsigned int version
;
156 unsigned int bootfrom
;
159 unsigned int args
[BINARY_MAX_ARGS
];
163 unsigned int dstaddr
;
164 unsigned int execaddr
;
165 unsigned int nandblksz
;
166 unsigned int nandbadblklocation
;
167 unsigned int nandeccmode
;
168 unsigned int nandpagesz
;
169 struct ext_hdr_v0_reg regdata
;
173 #define IMAGE_CFG_ELEMENT_MAX 256
176 * Byte 8 of the image header contains the version number. In the v0
177 * header, byte 8 was reserved, and always set to 0. In the v1 header,
178 * byte 8 has been changed to a proper field, set to 1.
180 static unsigned int image_version(void *header
)
182 unsigned char *ptr
= header
;
187 * Utility functions to manipulate boot mode and ecc modes (convert
188 * them back and forth between description strings and the
189 * corresponding numerical identifiers).
192 static const char *image_boot_mode_name(unsigned int id
)
195 for (i
= 0; boot_modes
[i
].name
; i
++)
196 if (boot_modes
[i
].id
== id
)
197 return boot_modes
[i
].name
;
201 int image_boot_mode_id(const char *boot_mode_name
)
204 for (i
= 0; boot_modes
[i
].name
; i
++)
205 if (!strcmp(boot_modes
[i
].name
, boot_mode_name
))
206 return boot_modes
[i
].id
;
211 int image_nand_ecc_mode_id(const char *nand_ecc_mode_name
)
214 for (i
= 0; nand_ecc_modes
[i
].name
; i
++)
215 if (!strcmp(nand_ecc_modes
[i
].name
, nand_ecc_mode_name
))
216 return nand_ecc_modes
[i
].id
;
220 static struct image_cfg_element
*
221 image_find_option(unsigned int optiontype
)
225 for (i
= 0; i
< cfgn
; i
++) {
226 if (image_cfg
[i
].type
== optiontype
)
227 return &image_cfg
[i
];
234 image_count_options(unsigned int optiontype
)
237 unsigned int count
= 0;
239 for (i
= 0; i
< cfgn
; i
++)
240 if (image_cfg
[i
].type
== optiontype
)
247 * Compute a 8-bit checksum of a memory area. This algorithm follows
248 * the requirements of the Marvell SoC BootROM specifications.
250 static uint8_t image_checksum8(void *start
, uint32_t len
)
255 /* check len and return zero checksum if invalid */
267 static uint32_t image_checksum32(void *start
, uint32_t len
)
272 /* check len and return zero checksum if invalid */
276 if (len
% sizeof(uint32_t)) {
277 fprintf(stderr
, "Length %d is not in multiple of %zu\n",
278 len
, sizeof(uint32_t));
285 len
-= sizeof(uint32_t);
291 static void *image_create_v0(size_t *imagesz
, struct image_tool_params
*params
,
294 struct image_cfg_element
*e
;
296 struct main_hdr_v0
*main_hdr
;
297 struct ext_hdr_v0
*ext_hdr
;
302 * Calculate the size of the header and the size of the
305 headersz
= sizeof(struct main_hdr_v0
);
307 if (image_count_options(IMAGE_CFG_DATA
) > 0) {
309 headersz
+= sizeof(struct ext_hdr_v0
);
312 if (image_count_options(IMAGE_CFG_PAYLOAD
) > 1) {
313 fprintf(stderr
, "More than one payload, not possible\n");
317 image
= malloc(headersz
);
319 fprintf(stderr
, "Cannot allocate memory for image\n");
323 memset(image
, 0, headersz
);
327 /* Fill in the main header */
328 main_hdr
->blocksize
= payloadsz
+ sizeof(uint32_t) - headersz
;
329 main_hdr
->srcaddr
= headersz
;
330 main_hdr
->ext
= has_ext
;
331 main_hdr
->destaddr
= params
->addr
;
332 main_hdr
->execaddr
= params
->ep
;
334 e
= image_find_option(IMAGE_CFG_BOOT_FROM
);
336 main_hdr
->blockid
= e
->bootfrom
;
337 e
= image_find_option(IMAGE_CFG_NAND_ECC_MODE
);
339 main_hdr
->nandeccmode
= e
->nandeccmode
;
340 e
= image_find_option(IMAGE_CFG_NAND_PAGESZ
);
342 main_hdr
->nandpagesize
= e
->nandpagesz
;
343 main_hdr
->checksum
= image_checksum8(image
,
344 sizeof(struct main_hdr_v0
));
346 /* Generate the ext header */
350 ext_hdr
= image
+ sizeof(struct main_hdr_v0
);
351 ext_hdr
->offset
= 0x40;
353 for (cfgi
= 0, datai
= 0; cfgi
< cfgn
; cfgi
++) {
354 e
= &image_cfg
[cfgi
];
355 if (e
->type
!= IMAGE_CFG_DATA
)
358 ext_hdr
->rcfg
[datai
].raddr
= e
->regdata
.raddr
;
359 ext_hdr
->rcfg
[datai
].rdata
= e
->regdata
.rdata
;
363 ext_hdr
->checksum
= image_checksum8(ext_hdr
,
364 sizeof(struct ext_hdr_v0
));
371 static size_t image_headersz_v1(struct image_tool_params
*params
,
374 struct image_cfg_element
*binarye
;
379 * Calculate the size of the header and the size of the
382 headersz
= sizeof(struct main_hdr_v1
);
384 if (image_count_options(IMAGE_CFG_BINARY
) > 1) {
385 fprintf(stderr
, "More than one binary blob, not supported\n");
389 if (image_count_options(IMAGE_CFG_PAYLOAD
) > 1) {
390 fprintf(stderr
, "More than one payload, not possible\n");
394 binarye
= image_find_option(IMAGE_CFG_BINARY
);
398 ret
= stat(binarye
->binary
.file
, &s
);
403 memset(cwd
, 0, sizeof(cwd
));
404 if (!getcwd(cwd
, sizeof(cwd
))) {
405 dir
= "current working directory";
406 perror("getcwd() failed");
410 "Didn't find the file '%s' in '%s' which is mandatory to generate the image\n"
411 "This file generally contains the DDR3 training code, and should be extracted from an existing bootable\n"
412 "image for your board. See 'kwbimage -x' to extract it from an existing image.\n",
413 binarye
->binary
.file
, dir
);
417 headersz
+= s
.st_size
+
418 binarye
->binary
.nargs
* sizeof(unsigned int);
424 * The payload should be aligned on some reasonable
427 return ALIGN_SUP(headersz
, 4096);
430 static void *image_create_v1(size_t *imagesz
, struct image_tool_params
*params
,
433 struct image_cfg_element
*e
, *binarye
;
434 struct main_hdr_v1
*main_hdr
;
441 * Calculate the size of the header and the size of the
444 headersz
= image_headersz_v1(params
, &hasext
);
448 image
= malloc(headersz
);
450 fprintf(stderr
, "Cannot allocate memory for image\n");
454 memset(image
, 0, headersz
);
456 cur
= main_hdr
= image
;
457 cur
+= sizeof(struct main_hdr_v1
);
459 /* Fill the main header */
460 main_hdr
->blocksize
= payloadsz
- headersz
+ sizeof(uint32_t);
461 main_hdr
->headersz_lsb
= headersz
& 0xFFFF;
462 main_hdr
->headersz_msb
= (headersz
& 0xFFFF0000) >> 16;
463 main_hdr
->destaddr
= params
->addr
;
464 main_hdr
->execaddr
= params
->ep
;
465 main_hdr
->srcaddr
= headersz
;
466 main_hdr
->ext
= hasext
;
467 main_hdr
->version
= 1;
468 e
= image_find_option(IMAGE_CFG_BOOT_FROM
);
470 main_hdr
->blockid
= e
->bootfrom
;
471 e
= image_find_option(IMAGE_CFG_NAND_BLKSZ
);
473 main_hdr
->nandblocksize
= e
->nandblksz
/ (64 * 1024);
474 e
= image_find_option(IMAGE_CFG_NAND_BADBLK_LOCATION
);
476 main_hdr
->nandbadblklocation
= e
->nandbadblklocation
;
478 binarye
= image_find_option(IMAGE_CFG_BINARY
);
480 struct opt_hdr_v1
*hdr
= cur
;
487 hdr
->headertype
= OPT_HDR_V1_BINARY_TYPE
;
489 bin
= fopen(binarye
->binary
.file
, "r");
491 fprintf(stderr
, "Cannot open binary file %s\n",
492 binarye
->binary
.file
);
496 fstat(fileno(bin
), &s
);
498 binhdrsz
= sizeof(struct opt_hdr_v1
) +
499 (binarye
->binary
.nargs
+ 1) * sizeof(unsigned int) +
501 hdr
->headersz_lsb
= binhdrsz
& 0xFFFF;
502 hdr
->headersz_msb
= (binhdrsz
& 0xFFFF0000) >> 16;
504 cur
+= sizeof(struct opt_hdr_v1
);
507 *args
= binarye
->binary
.nargs
;
509 for (argi
= 0; argi
< binarye
->binary
.nargs
; argi
++)
510 args
[argi
] = binarye
->binary
.args
[argi
];
512 cur
+= (binarye
->binary
.nargs
+ 1) * sizeof(unsigned int);
514 ret
= fread(cur
, s
.st_size
, 1, bin
);
517 "Could not read binary image %s\n",
518 binarye
->binary
.file
);
527 * For now, we don't support more than one binary
528 * header, and no other header types are
529 * supported. So, the binary header is necessarily the
532 *((unsigned char *)cur
) = 0;
534 cur
+= sizeof(uint32_t);
537 /* Calculate and set the header checksum */
538 main_hdr
->checksum
= image_checksum8(main_hdr
, headersz
);
544 static int image_create_config_parse_oneline(char *line
,
545 struct image_cfg_element
*el
)
547 char *keyword
, *saveptr
;
548 char deliminiters
[] = " \t";
550 keyword
= strtok_r(line
, deliminiters
, &saveptr
);
551 if (!strcmp(keyword
, "VERSION")) {
552 char *value
= strtok_r(NULL
, deliminiters
, &saveptr
);
553 el
->type
= IMAGE_CFG_VERSION
;
554 el
->version
= atoi(value
);
555 } else if (!strcmp(keyword
, "BOOT_FROM")) {
556 char *value
= strtok_r(NULL
, deliminiters
, &saveptr
);
557 el
->type
= IMAGE_CFG_BOOT_FROM
;
558 el
->bootfrom
= image_boot_mode_id(value
);
559 if (el
->bootfrom
< 0) {
561 "Invalid boot media '%s'\n", value
);
564 } else if (!strcmp(keyword
, "NAND_BLKSZ")) {
565 char *value
= strtok_r(NULL
, deliminiters
, &saveptr
);
566 el
->type
= IMAGE_CFG_NAND_BLKSZ
;
567 el
->nandblksz
= strtoul(value
, NULL
, 16);
568 } else if (!strcmp(keyword
, "NAND_BADBLK_LOCATION")) {
569 char *value
= strtok_r(NULL
, deliminiters
, &saveptr
);
570 el
->type
= IMAGE_CFG_NAND_BADBLK_LOCATION
;
571 el
->nandbadblklocation
=
572 strtoul(value
, NULL
, 16);
573 } else if (!strcmp(keyword
, "NAND_ECC_MODE")) {
574 char *value
= strtok_r(NULL
, deliminiters
, &saveptr
);
575 el
->type
= IMAGE_CFG_NAND_ECC_MODE
;
576 el
->nandeccmode
= image_nand_ecc_mode_id(value
);
577 if (el
->nandeccmode
< 0) {
579 "Invalid NAND ECC mode '%s'\n", value
);
582 } else if (!strcmp(keyword
, "NAND_PAGE_SIZE")) {
583 char *value
= strtok_r(NULL
, deliminiters
, &saveptr
);
584 el
->type
= IMAGE_CFG_NAND_PAGESZ
;
585 el
->nandpagesz
= strtoul(value
, NULL
, 16);
586 } else if (!strcmp(keyword
, "BINARY")) {
587 char *value
= strtok_r(NULL
, deliminiters
, &saveptr
);
590 el
->type
= IMAGE_CFG_BINARY
;
591 el
->binary
.file
= strdup(value
);
593 value
= strtok_r(NULL
, deliminiters
, &saveptr
);
596 el
->binary
.args
[argi
] = strtoul(value
, NULL
, 16);
598 if (argi
>= BINARY_MAX_ARGS
) {
600 "Too many argument for binary\n");
604 el
->binary
.nargs
= argi
;
605 } else if (!strcmp(keyword
, "DATA")) {
606 char *value1
= strtok_r(NULL
, deliminiters
, &saveptr
);
607 char *value2
= strtok_r(NULL
, deliminiters
, &saveptr
);
609 if (!value1
|| !value2
) {
611 "Invalid number of arguments for DATA\n");
615 el
->type
= IMAGE_CFG_DATA
;
616 el
->regdata
.raddr
= strtoul(value1
, NULL
, 16);
617 el
->regdata
.rdata
= strtoul(value2
, NULL
, 16);
619 fprintf(stderr
, "Ignoring unknown line '%s'\n", line
);
626 * Parse the configuration file 'fcfg' into the array of configuration
627 * elements 'image_cfg', and return the number of configuration
628 * elements in 'cfgn'.
630 static int image_create_config_parse(FILE *fcfg
)
635 /* Parse the configuration file */
636 while (!feof(fcfg
)) {
640 /* Read the current line */
641 memset(buf
, 0, sizeof(buf
));
642 line
= fgets(buf
, sizeof(buf
), fcfg
);
646 /* Ignore useless lines */
647 if (line
[0] == '\n' || line
[0] == '#')
650 /* Strip final newline */
651 if (line
[strlen(line
) - 1] == '\n')
652 line
[strlen(line
) - 1] = 0;
654 /* Parse the current line */
655 ret
= image_create_config_parse_oneline(line
,
662 if (cfgi
>= IMAGE_CFG_ELEMENT_MAX
) {
664 "Too many configuration elements in .cfg file\n");
673 static int image_get_version(void)
675 struct image_cfg_element
*e
;
677 e
= image_find_option(IMAGE_CFG_VERSION
);
684 static int image_version_file(const char *input
)
690 fcfg
= fopen(input
, "r");
692 fprintf(stderr
, "Could not open input file %s\n", input
);
696 image_cfg
= malloc(IMAGE_CFG_ELEMENT_MAX
*
697 sizeof(struct image_cfg_element
));
699 fprintf(stderr
, "Cannot allocate memory\n");
705 IMAGE_CFG_ELEMENT_MAX
* sizeof(struct image_cfg_element
));
708 ret
= image_create_config_parse(fcfg
);
715 version
= image_get_version();
716 /* Fallback to version 0 is no version is provided in the cfg file */
725 static void kwbimage_set_header(void *ptr
, struct stat
*sbuf
, int ifd
,
726 struct image_tool_params
*params
)
736 fcfg
= fopen(params
->imagename
, "r");
738 fprintf(stderr
, "Could not open input file %s\n",
743 image_cfg
= malloc(IMAGE_CFG_ELEMENT_MAX
*
744 sizeof(struct image_cfg_element
));
746 fprintf(stderr
, "Cannot allocate memory\n");
752 IMAGE_CFG_ELEMENT_MAX
* sizeof(struct image_cfg_element
));
755 ret
= image_create_config_parse(fcfg
);
762 version
= image_get_version();
765 * Fallback to version 0 if no version is provided in the
770 image
= image_create_v0(&headersz
, params
, sbuf
->st_size
);
774 image
= image_create_v1(&headersz
, params
, sbuf
->st_size
);
778 fprintf(stderr
, "Unsupported version %d\n", version
);
784 fprintf(stderr
, "Could not create image\n");
791 /* Build and add image checksum header */
792 checksum
= image_checksum32((uint32_t *)ptr
, sbuf
->st_size
);
793 size
= write(ifd
, &checksum
, sizeof(uint32_t));
794 if (size
!= sizeof(uint32_t)) {
795 fprintf(stderr
, "Error:%s - Checksum write %d bytes %s\n",
796 params
->cmdname
, size
, params
->imagefile
);
800 sbuf
->st_size
+= sizeof(uint32_t);
802 /* Finally copy the header into the image area */
803 memcpy(ptr
, image
, headersz
);
808 static void kwbimage_print_header(const void *ptr
)
810 struct main_hdr_v0
*mhdr
= (struct main_hdr_v0
*)ptr
;
812 printf("Image Type: MVEBU Boot from %s Image\n",
813 image_boot_mode_name(mhdr
->blockid
));
814 printf("Image version:%d\n", image_version((void *)ptr
));
815 printf("Data Size: ");
816 genimg_print_size(mhdr
->blocksize
- sizeof(uint32_t));
817 printf("Load Address: %08x\n", mhdr
->destaddr
);
818 printf("Entry Point: %08x\n", mhdr
->execaddr
);
821 static int kwbimage_check_image_types(uint8_t type
)
823 if (type
== IH_TYPE_KWBIMAGE
)
829 static int kwbimage_verify_header(unsigned char *ptr
, int image_size
,
830 struct image_tool_params
*params
)
832 struct main_hdr_v0
*main_hdr
;
833 struct ext_hdr_v0
*ext_hdr
;
836 main_hdr
= (void *)ptr
;
837 checksum
= image_checksum8(ptr
,
838 sizeof(struct main_hdr_v0
)
840 if (checksum
!= main_hdr
->checksum
)
841 return -FDT_ERR_BADSTRUCTURE
;
843 /* Only version 0 extended header has checksum */
844 if (image_version((void *)ptr
) == 0) {
845 ext_hdr
= (void *)ptr
+ sizeof(struct main_hdr_v0
);
846 checksum
= image_checksum8(ext_hdr
,
847 sizeof(struct ext_hdr_v0
)
849 if (checksum
!= ext_hdr
->checksum
)
850 return -FDT_ERR_BADSTRUCTURE
;
856 static int kwbimage_generate(struct image_tool_params
*params
,
857 struct image_type_params
*tparams
)
863 version
= image_version_file(params
->imagename
);
865 alloc_len
= sizeof(struct main_hdr_v0
) +
866 sizeof(struct ext_hdr_v0
);
868 alloc_len
= image_headersz_v1(params
, NULL
);
871 hdr
= malloc(alloc_len
);
873 fprintf(stderr
, "%s: malloc return failure: %s\n",
874 params
->cmdname
, strerror(errno
));
878 memset(hdr
, 0, alloc_len
);
879 tparams
->header_size
= alloc_len
;
886 * Report Error if xflag is set in addition to default
888 static int kwbimage_check_params(struct image_tool_params
*params
)
890 if (!strlen(params
->imagename
)) {
891 fprintf(stderr
, "Error:%s - Configuration file not specified, "
892 "it is needed for kwbimage generation\n",
897 return (params
->dflag
&& (params
->fflag
|| params
->lflag
)) ||
898 (params
->fflag
&& (params
->dflag
|| params
->lflag
)) ||
899 (params
->lflag
&& (params
->dflag
|| params
->fflag
)) ||
900 (params
->xflag
) || !(strlen(params
->imagename
));
904 * kwbimage type parameters definition
906 static struct image_type_params kwbimage_params
= {
907 .name
= "Marvell MVEBU Boot Image support",
908 .header_size
= 0, /* no fixed header size */
910 .vrec_header
= kwbimage_generate
,
911 .check_image_type
= kwbimage_check_image_types
,
912 .verify_header
= kwbimage_verify_header
,
913 .print_header
= kwbimage_print_header
,
914 .set_header
= kwbimage_set_header
,
915 .check_params
= kwbimage_check_params
,
918 void init_kwb_image_type (void)
920 register_image_type(&kwbimage_params
);