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 static struct image_cfg_element
*image_cfg
;
28 struct boot_mode boot_modes
[] = {
39 struct nand_ecc_mode
{
44 struct nand_ecc_mode nand_ecc_modes
[] = {
52 /* Used to identify an undefined execution or destination address */
53 #define ADDR_INVALID ((uint32_t)-1)
55 #define BINARY_MAX_ARGS 8
57 /* In-memory representation of a line of the configuration file */
58 struct image_cfg_element
{
60 IMAGE_CFG_VERSION
= 0x1,
65 IMAGE_CFG_NAND_BADBLK_LOCATION
,
66 IMAGE_CFG_NAND_ECC_MODE
,
67 IMAGE_CFG_NAND_PAGESZ
,
74 unsigned int bootfrom
;
77 unsigned int args
[BINARY_MAX_ARGS
];
82 unsigned int execaddr
;
83 unsigned int nandblksz
;
84 unsigned int nandbadblklocation
;
85 unsigned int nandeccmode
;
86 unsigned int nandpagesz
;
87 struct ext_hdr_v0_reg regdata
;
91 #define IMAGE_CFG_ELEMENT_MAX 256
94 * Utility functions to manipulate boot mode and ecc modes (convert
95 * them back and forth between description strings and the
96 * corresponding numerical identifiers).
99 static const char *image_boot_mode_name(unsigned int id
)
102 for (i
= 0; boot_modes
[i
].name
; i
++)
103 if (boot_modes
[i
].id
== id
)
104 return boot_modes
[i
].name
;
108 int image_boot_mode_id(const char *boot_mode_name
)
111 for (i
= 0; boot_modes
[i
].name
; i
++)
112 if (!strcmp(boot_modes
[i
].name
, boot_mode_name
))
113 return boot_modes
[i
].id
;
118 int image_nand_ecc_mode_id(const char *nand_ecc_mode_name
)
121 for (i
= 0; nand_ecc_modes
[i
].name
; i
++)
122 if (!strcmp(nand_ecc_modes
[i
].name
, nand_ecc_mode_name
))
123 return nand_ecc_modes
[i
].id
;
127 static struct image_cfg_element
*
128 image_find_option(unsigned int optiontype
)
132 for (i
= 0; i
< cfgn
; i
++) {
133 if (image_cfg
[i
].type
== optiontype
)
134 return &image_cfg
[i
];
141 image_count_options(unsigned int optiontype
)
144 unsigned int count
= 0;
146 for (i
= 0; i
< cfgn
; i
++)
147 if (image_cfg
[i
].type
== optiontype
)
154 * Compute a 8-bit checksum of a memory area. This algorithm follows
155 * the requirements of the Marvell SoC BootROM specifications.
157 static uint8_t image_checksum8(void *start
, uint32_t len
)
162 /* check len and return zero checksum if invalid */
174 static uint32_t image_checksum32(void *start
, uint32_t len
)
179 /* check len and return zero checksum if invalid */
183 if (len
% sizeof(uint32_t)) {
184 fprintf(stderr
, "Length %d is not in multiple of %zu\n",
185 len
, sizeof(uint32_t));
192 len
-= sizeof(uint32_t);
198 static void *image_create_v0(size_t *imagesz
, struct image_tool_params
*params
,
201 struct image_cfg_element
*e
;
203 struct main_hdr_v0
*main_hdr
;
204 struct ext_hdr_v0
*ext_hdr
;
209 * Calculate the size of the header and the size of the
212 headersz
= sizeof(struct main_hdr_v0
);
214 if (image_count_options(IMAGE_CFG_DATA
) > 0) {
216 headersz
+= sizeof(struct ext_hdr_v0
);
219 if (image_count_options(IMAGE_CFG_PAYLOAD
) > 1) {
220 fprintf(stderr
, "More than one payload, not possible\n");
224 image
= malloc(headersz
);
226 fprintf(stderr
, "Cannot allocate memory for image\n");
230 memset(image
, 0, headersz
);
234 /* Fill in the main header */
235 main_hdr
->blocksize
= payloadsz
+ sizeof(uint32_t) - headersz
;
236 main_hdr
->srcaddr
= headersz
;
237 main_hdr
->ext
= has_ext
;
238 main_hdr
->destaddr
= params
->addr
;
239 main_hdr
->execaddr
= params
->ep
;
241 e
= image_find_option(IMAGE_CFG_BOOT_FROM
);
243 main_hdr
->blockid
= e
->bootfrom
;
244 e
= image_find_option(IMAGE_CFG_NAND_ECC_MODE
);
246 main_hdr
->nandeccmode
= e
->nandeccmode
;
247 e
= image_find_option(IMAGE_CFG_NAND_PAGESZ
);
249 main_hdr
->nandpagesize
= e
->nandpagesz
;
250 main_hdr
->checksum
= image_checksum8(image
,
251 sizeof(struct main_hdr_v0
));
253 /* Generate the ext header */
257 ext_hdr
= image
+ sizeof(struct main_hdr_v0
);
258 ext_hdr
->offset
= 0x40;
260 for (cfgi
= 0, datai
= 0; cfgi
< cfgn
; cfgi
++) {
261 e
= &image_cfg
[cfgi
];
262 if (e
->type
!= IMAGE_CFG_DATA
)
265 ext_hdr
->rcfg
[datai
].raddr
= e
->regdata
.raddr
;
266 ext_hdr
->rcfg
[datai
].rdata
= e
->regdata
.rdata
;
270 ext_hdr
->checksum
= image_checksum8(ext_hdr
,
271 sizeof(struct ext_hdr_v0
));
278 static size_t image_headersz_v1(struct image_tool_params
*params
,
281 struct image_cfg_element
*binarye
;
286 * Calculate the size of the header and the size of the
289 headersz
= sizeof(struct main_hdr_v1
);
291 if (image_count_options(IMAGE_CFG_BINARY
) > 1) {
292 fprintf(stderr
, "More than one binary blob, not supported\n");
296 if (image_count_options(IMAGE_CFG_PAYLOAD
) > 1) {
297 fprintf(stderr
, "More than one payload, not possible\n");
301 binarye
= image_find_option(IMAGE_CFG_BINARY
);
305 ret
= stat(binarye
->binary
.file
, &s
);
310 memset(cwd
, 0, sizeof(cwd
));
311 if (!getcwd(cwd
, sizeof(cwd
))) {
312 dir
= "current working directory";
313 perror("getcwd() failed");
317 "Didn't find the file '%s' in '%s' which is mandatory to generate the image\n"
318 "This file generally contains the DDR3 training code, and should be extracted from an existing bootable\n"
319 "image for your board. See 'kwbimage -x' to extract it from an existing image.\n",
320 binarye
->binary
.file
, dir
);
324 headersz
+= s
.st_size
+
325 binarye
->binary
.nargs
* sizeof(unsigned int);
330 #if defined(CONFIG_SYS_U_BOOT_OFFS)
331 if (headersz
> CONFIG_SYS_U_BOOT_OFFS
) {
332 fprintf(stderr
, "Error: Image header (incl. SPL image) too big!\n");
333 fprintf(stderr
, "header=0x%x CONFIG_SYS_U_BOOT_OFFS=0x%x!\n",
334 (int)headersz
, CONFIG_SYS_U_BOOT_OFFS
);
335 fprintf(stderr
, "Increase CONFIG_SYS_U_BOOT_OFFS!\n");
338 headersz
= CONFIG_SYS_U_BOOT_OFFS
;
343 * The payload should be aligned on some reasonable
346 return ALIGN_SUP(headersz
, 4096);
349 static void *image_create_v1(size_t *imagesz
, struct image_tool_params
*params
,
352 struct image_cfg_element
*e
, *binarye
;
353 struct main_hdr_v1
*main_hdr
;
360 * Calculate the size of the header and the size of the
363 headersz
= image_headersz_v1(params
, &hasext
);
367 image
= malloc(headersz
);
369 fprintf(stderr
, "Cannot allocate memory for image\n");
373 memset(image
, 0, headersz
);
375 cur
= main_hdr
= image
;
376 cur
+= sizeof(struct main_hdr_v1
);
378 /* Fill the main header */
379 main_hdr
->blocksize
= payloadsz
- headersz
+ sizeof(uint32_t);
380 main_hdr
->headersz_lsb
= headersz
& 0xFFFF;
381 main_hdr
->headersz_msb
= (headersz
& 0xFFFF0000) >> 16;
382 main_hdr
->destaddr
= params
->addr
;
383 main_hdr
->execaddr
= params
->ep
;
384 main_hdr
->srcaddr
= headersz
;
385 main_hdr
->ext
= hasext
;
386 main_hdr
->version
= 1;
387 e
= image_find_option(IMAGE_CFG_BOOT_FROM
);
389 main_hdr
->blockid
= e
->bootfrom
;
390 e
= image_find_option(IMAGE_CFG_NAND_BLKSZ
);
392 main_hdr
->nandblocksize
= e
->nandblksz
/ (64 * 1024);
393 e
= image_find_option(IMAGE_CFG_NAND_BADBLK_LOCATION
);
395 main_hdr
->nandbadblklocation
= e
->nandbadblklocation
;
397 binarye
= image_find_option(IMAGE_CFG_BINARY
);
399 struct opt_hdr_v1
*hdr
= cur
;
406 hdr
->headertype
= OPT_HDR_V1_BINARY_TYPE
;
408 bin
= fopen(binarye
->binary
.file
, "r");
410 fprintf(stderr
, "Cannot open binary file %s\n",
411 binarye
->binary
.file
);
415 fstat(fileno(bin
), &s
);
417 binhdrsz
= sizeof(struct opt_hdr_v1
) +
418 (binarye
->binary
.nargs
+ 1) * sizeof(unsigned int) +
420 binhdrsz
= ALIGN_SUP(binhdrsz
, 32);
421 hdr
->headersz_lsb
= binhdrsz
& 0xFFFF;
422 hdr
->headersz_msb
= (binhdrsz
& 0xFFFF0000) >> 16;
424 cur
+= sizeof(struct opt_hdr_v1
);
427 *args
= binarye
->binary
.nargs
;
429 for (argi
= 0; argi
< binarye
->binary
.nargs
; argi
++)
430 args
[argi
] = binarye
->binary
.args
[argi
];
432 cur
+= (binarye
->binary
.nargs
+ 1) * sizeof(unsigned int);
434 ret
= fread(cur
, s
.st_size
, 1, bin
);
437 "Could not read binary image %s\n",
438 binarye
->binary
.file
);
447 * For now, we don't support more than one binary
448 * header, and no other header types are
449 * supported. So, the binary header is necessarily the
452 *((unsigned char *)cur
) = 0;
454 cur
+= sizeof(uint32_t);
457 /* Calculate and set the header checksum */
458 main_hdr
->checksum
= image_checksum8(main_hdr
, headersz
);
464 static int image_create_config_parse_oneline(char *line
,
465 struct image_cfg_element
*el
)
467 char *keyword
, *saveptr
;
468 char deliminiters
[] = " \t";
470 keyword
= strtok_r(line
, deliminiters
, &saveptr
);
471 if (!strcmp(keyword
, "VERSION")) {
472 char *value
= strtok_r(NULL
, deliminiters
, &saveptr
);
473 el
->type
= IMAGE_CFG_VERSION
;
474 el
->version
= atoi(value
);
475 } else if (!strcmp(keyword
, "BOOT_FROM")) {
476 char *value
= strtok_r(NULL
, deliminiters
, &saveptr
);
477 int ret
= image_boot_mode_id(value
);
480 "Invalid boot media '%s'\n", value
);
483 el
->type
= IMAGE_CFG_BOOT_FROM
;
485 } else if (!strcmp(keyword
, "NAND_BLKSZ")) {
486 char *value
= strtok_r(NULL
, deliminiters
, &saveptr
);
487 el
->type
= IMAGE_CFG_NAND_BLKSZ
;
488 el
->nandblksz
= strtoul(value
, NULL
, 16);
489 } else if (!strcmp(keyword
, "NAND_BADBLK_LOCATION")) {
490 char *value
= strtok_r(NULL
, deliminiters
, &saveptr
);
491 el
->type
= IMAGE_CFG_NAND_BADBLK_LOCATION
;
492 el
->nandbadblklocation
=
493 strtoul(value
, NULL
, 16);
494 } else if (!strcmp(keyword
, "NAND_ECC_MODE")) {
495 char *value
= strtok_r(NULL
, deliminiters
, &saveptr
);
496 int ret
= image_nand_ecc_mode_id(value
);
499 "Invalid NAND ECC mode '%s'\n", value
);
502 el
->type
= IMAGE_CFG_NAND_ECC_MODE
;
503 el
->nandeccmode
= ret
;
504 } else if (!strcmp(keyword
, "NAND_PAGE_SIZE")) {
505 char *value
= strtok_r(NULL
, deliminiters
, &saveptr
);
506 el
->type
= IMAGE_CFG_NAND_PAGESZ
;
507 el
->nandpagesz
= strtoul(value
, NULL
, 16);
508 } else if (!strcmp(keyword
, "BINARY")) {
509 char *value
= strtok_r(NULL
, deliminiters
, &saveptr
);
512 el
->type
= IMAGE_CFG_BINARY
;
513 el
->binary
.file
= strdup(value
);
515 value
= strtok_r(NULL
, deliminiters
, &saveptr
);
518 el
->binary
.args
[argi
] = strtoul(value
, NULL
, 16);
520 if (argi
>= BINARY_MAX_ARGS
) {
522 "Too many argument for binary\n");
526 el
->binary
.nargs
= argi
;
527 } else if (!strcmp(keyword
, "DATA")) {
528 char *value1
= strtok_r(NULL
, deliminiters
, &saveptr
);
529 char *value2
= strtok_r(NULL
, deliminiters
, &saveptr
);
531 if (!value1
|| !value2
) {
533 "Invalid number of arguments for DATA\n");
537 el
->type
= IMAGE_CFG_DATA
;
538 el
->regdata
.raddr
= strtoul(value1
, NULL
, 16);
539 el
->regdata
.rdata
= strtoul(value2
, NULL
, 16);
541 fprintf(stderr
, "Ignoring unknown line '%s'\n", line
);
548 * Parse the configuration file 'fcfg' into the array of configuration
549 * elements 'image_cfg', and return the number of configuration
550 * elements in 'cfgn'.
552 static int image_create_config_parse(FILE *fcfg
)
557 /* Parse the configuration file */
558 while (!feof(fcfg
)) {
562 /* Read the current line */
563 memset(buf
, 0, sizeof(buf
));
564 line
= fgets(buf
, sizeof(buf
), fcfg
);
568 /* Ignore useless lines */
569 if (line
[0] == '\n' || line
[0] == '#')
572 /* Strip final newline */
573 if (line
[strlen(line
) - 1] == '\n')
574 line
[strlen(line
) - 1] = 0;
576 /* Parse the current line */
577 ret
= image_create_config_parse_oneline(line
,
584 if (cfgi
>= IMAGE_CFG_ELEMENT_MAX
) {
586 "Too many configuration elements in .cfg file\n");
595 static int image_get_version(void)
597 struct image_cfg_element
*e
;
599 e
= image_find_option(IMAGE_CFG_VERSION
);
606 static int image_version_file(const char *input
)
612 fcfg
= fopen(input
, "r");
614 fprintf(stderr
, "Could not open input file %s\n", input
);
618 image_cfg
= malloc(IMAGE_CFG_ELEMENT_MAX
*
619 sizeof(struct image_cfg_element
));
621 fprintf(stderr
, "Cannot allocate memory\n");
627 IMAGE_CFG_ELEMENT_MAX
* sizeof(struct image_cfg_element
));
630 ret
= image_create_config_parse(fcfg
);
637 version
= image_get_version();
638 /* Fallback to version 0 is no version is provided in the cfg file */
647 static void kwbimage_set_header(void *ptr
, struct stat
*sbuf
, int ifd
,
648 struct image_tool_params
*params
)
658 fcfg
= fopen(params
->imagename
, "r");
660 fprintf(stderr
, "Could not open input file %s\n",
665 image_cfg
= malloc(IMAGE_CFG_ELEMENT_MAX
*
666 sizeof(struct image_cfg_element
));
668 fprintf(stderr
, "Cannot allocate memory\n");
674 IMAGE_CFG_ELEMENT_MAX
* sizeof(struct image_cfg_element
));
677 ret
= image_create_config_parse(fcfg
);
684 version
= image_get_version();
687 * Fallback to version 0 if no version is provided in the
692 image
= image_create_v0(&headersz
, params
, sbuf
->st_size
);
696 image
= image_create_v1(&headersz
, params
, sbuf
->st_size
);
700 fprintf(stderr
, "Unsupported version %d\n", version
);
706 fprintf(stderr
, "Could not create image\n");
713 /* Build and add image checksum header */
714 checksum
= image_checksum32((uint32_t *)ptr
, sbuf
->st_size
);
715 size
= write(ifd
, &checksum
, sizeof(uint32_t));
716 if (size
!= sizeof(uint32_t)) {
717 fprintf(stderr
, "Error:%s - Checksum write %d bytes %s\n",
718 params
->cmdname
, size
, params
->imagefile
);
722 sbuf
->st_size
+= sizeof(uint32_t);
724 /* Finally copy the header into the image area */
725 memcpy(ptr
, image
, headersz
);
730 static void kwbimage_print_header(const void *ptr
)
732 struct main_hdr_v0
*mhdr
= (struct main_hdr_v0
*)ptr
;
734 printf("Image Type: MVEBU Boot from %s Image\n",
735 image_boot_mode_name(mhdr
->blockid
));
736 printf("Image version:%d\n", image_version((void *)ptr
));
737 printf("Data Size: ");
738 genimg_print_size(mhdr
->blocksize
- sizeof(uint32_t));
739 printf("Load Address: %08x\n", mhdr
->destaddr
);
740 printf("Entry Point: %08x\n", mhdr
->execaddr
);
743 static int kwbimage_check_image_types(uint8_t type
)
745 if (type
== IH_TYPE_KWBIMAGE
)
751 static int kwbimage_verify_header(unsigned char *ptr
, int image_size
,
752 struct image_tool_params
*params
)
754 struct main_hdr_v0
*main_hdr
;
755 struct ext_hdr_v0
*ext_hdr
;
758 main_hdr
= (void *)ptr
;
759 checksum
= image_checksum8(ptr
,
760 sizeof(struct main_hdr_v0
)
762 if (checksum
!= main_hdr
->checksum
)
763 return -FDT_ERR_BADSTRUCTURE
;
765 /* Only version 0 extended header has checksum */
766 if (image_version((void *)ptr
) == 0) {
767 ext_hdr
= (void *)ptr
+ sizeof(struct main_hdr_v0
);
768 checksum
= image_checksum8(ext_hdr
,
769 sizeof(struct ext_hdr_v0
)
771 if (checksum
!= ext_hdr
->checksum
)
772 return -FDT_ERR_BADSTRUCTURE
;
778 static int kwbimage_generate(struct image_tool_params
*params
,
779 struct image_type_params
*tparams
)
785 version
= image_version_file(params
->imagename
);
787 alloc_len
= sizeof(struct main_hdr_v0
) +
788 sizeof(struct ext_hdr_v0
);
790 alloc_len
= image_headersz_v1(params
, NULL
);
793 hdr
= malloc(alloc_len
);
795 fprintf(stderr
, "%s: malloc return failure: %s\n",
796 params
->cmdname
, strerror(errno
));
800 memset(hdr
, 0, alloc_len
);
801 tparams
->header_size
= alloc_len
;
808 * Report Error if xflag is set in addition to default
810 static int kwbimage_check_params(struct image_tool_params
*params
)
812 if (!strlen(params
->imagename
)) {
813 fprintf(stderr
, "Error:%s - Configuration file not specified, "
814 "it is needed for kwbimage generation\n",
819 return (params
->dflag
&& (params
->fflag
|| params
->lflag
)) ||
820 (params
->fflag
&& (params
->dflag
|| params
->lflag
)) ||
821 (params
->lflag
&& (params
->dflag
|| params
->fflag
)) ||
822 (params
->xflag
) || !(strlen(params
->imagename
));
826 * kwbimage type parameters definition
830 "Marvell MVEBU Boot Image support",
833 kwbimage_check_params
,
834 kwbimage_verify_header
,
835 kwbimage_print_header
,
838 kwbimage_check_image_types
,