barebox/scripts/kwbimage.c
Lucas Stach 9c63e92baa treewide: fix signedness mixups in printf format specifiers
This most likely doesn't fix any real bugs, but it's the
right thing to do and reduces the noise level with static
checkers.

Signed-off-by: Lucas Stach <dev@lynxeye.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
2014-04-23 09:05:51 +02:00

1508 lines
36 KiB
C

/*
* Image manipulator for Marvell SoCs
* supports Kirkwood, Dove, Armada 370, and Armada XP
*
* (C) Copyright 2013 Thomas Petazzoni
* <thomas.petazzoni@free-electrons.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This tool allows to extract and create bootable images for Marvell
* Kirkwood, Dove, Armada 370, and Armada XP SoCs. It supports two
* versions of the bootable image format: version 0 (used on Marvell
* Kirkwood and Dove) and version 1 (used on Marvell Armada 370/XP).
*
* To extract an image, run:
* ./scripts/kwbimage -x -i <image-file> -o <some-directory>
*
* In <some-directory>, kwbimage will output 'kwbimage.cfg', the
* configuration file that describes the image, 'payload', which is
* the bootloader code itself, and it may output a 'binary.0' file
* that corresponds to a binary blob (only possible in version 1
* images).
*
* To create an image, run:
* ./scripts/kwbimage -c -i <image-cfg-file> -o <image-file>
*
* The given configuration file is in the format of the 'kwbimage.cfg'
* file, and should reference the payload file (generally the
* bootloader code) and optionally a binary blob.
*
* Not implemented: support for the register headers and secure
* headers in v1 images
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define ALIGN_SUP(x, a) (((x) + (a - 1)) & ~(a - 1))
/* Structure of the main header, version 0 (Kirkwood, Dove) */
struct main_hdr_v0 {
uint8_t blockid; /*0 */
uint8_t nandeccmode; /*1 */
uint16_t nandpagesize; /*2-3 */
uint32_t blocksize; /*4-7 */
uint32_t rsvd1; /*8-11 */
uint32_t srcaddr; /*12-15 */
uint32_t destaddr; /*16-19 */
uint32_t execaddr; /*20-23 */
uint8_t satapiomode; /*24 */
uint8_t rsvd3; /*25 */
uint16_t ddrinitdelay; /*26-27 */
uint16_t rsvd2; /*28-29 */
uint8_t ext; /*30 */
uint8_t checksum; /*31 */
};
struct ext_hdr_v0_reg {
uint32_t raddr;
uint32_t rdata;
};
#define EXT_HDR_V0_REG_COUNT ((0x1dc - 0x20)/sizeof(struct ext_hdr_v0_reg))
struct ext_hdr_v0 {
uint32_t offset;
uint8_t reserved[0x20 - sizeof(uint32_t)];
struct ext_hdr_v0_reg rcfg[EXT_HDR_V0_REG_COUNT];
uint8_t reserved2[7];
uint8_t checksum;
};
/* Structure of the main header, version 1 (Armada 370, Armada XP) */
struct main_hdr_v1 {
uint8_t blockid; /* 0 */
uint8_t reserved1; /* 1 */
uint16_t reserved2; /* 2-3 */
uint32_t blocksize; /* 4-7 */
uint8_t version; /* 8 */
uint8_t headersz_msb; /* 9 */
uint16_t headersz_lsb; /* A-B */
uint32_t srcaddr; /* C-F */
uint32_t destaddr; /* 10-13 */
uint32_t execaddr; /* 14-17 */
uint8_t reserved3; /* 18 */
uint8_t nandblocksize; /* 19 */
uint8_t nandbadblklocation; /* 1A */
uint8_t reserved4; /* 1B */
uint16_t reserved5; /* 1C-1D */
uint8_t ext; /* 1E */
uint8_t checksum; /* 1F */
};
/*
* Header for the optional headers, version 1 (Armada 370, Armada XP)
*/
struct opt_hdr_v1 {
uint8_t headertype;
uint8_t headersz_msb;
uint16_t headersz_lsb;
char data[0];
};
/*
* Various values for the opt_hdr_v1->headertype field, describing the
* different types of optional headers. The "secure" header contains
* informations related to secure boot (encryption keys, etc.). The
* "binary" header contains ARM binary code to be executed prior to
* executing the main payload (usually the bootloader). This is
* typically used to execute DDR3 training code. The "register" header
* allows to describe a set of (address, value) tuples that are
* generally used to configure the DRAM controller.
*/
#define OPT_HDR_V1_SECURE_TYPE 0x1
#define OPT_HDR_V1_BINARY_TYPE 0x2
#define OPT_HDR_V1_REGISTER_TYPE 0x3
#define KWBHEADER_V1_SIZE(hdr) \
(((hdr)->headersz_msb << 16) | (hdr)->headersz_lsb)
struct boot_mode {
unsigned int id;
const char *name;
};
struct boot_mode boot_modes[] = {
{ 0x4D, "i2c" },
{ 0x5A, "spi" },
{ 0x8B, "nand" },
{ 0x78, "sata" },
{ 0x9C, "pex" },
{ 0x69, "uart" },
{},
};
struct nand_ecc_mode {
unsigned int id;
const char *name;
};
struct nand_ecc_mode nand_ecc_modes[] = {
{ 0x00, "default" },
{ 0x01, "hamming" },
{ 0x02, "rs" },
{ 0x03, "disabled" },
{},
};
/* Used to identify an undefined execution or destination address */
#define ADDR_INVALID ((uint32_t)-1)
#define BINARY_MAX_ARGS 8
/* In-memory representation of a line of the configuration file */
struct image_cfg_element {
enum {
IMAGE_CFG_VERSION = 0x1,
IMAGE_CFG_BOOT_FROM,
IMAGE_CFG_DEST_ADDR,
IMAGE_CFG_EXEC_ADDR,
IMAGE_CFG_NAND_BLKSZ,
IMAGE_CFG_NAND_BADBLK_LOCATION,
IMAGE_CFG_NAND_ECC_MODE,
IMAGE_CFG_NAND_PAGESZ,
IMAGE_CFG_BINARY,
IMAGE_CFG_PAYLOAD,
IMAGE_CFG_DATA,
} type;
union {
unsigned int version;
unsigned int bootfrom;
struct {
const char *file;
unsigned int args[BINARY_MAX_ARGS];
unsigned int nargs;
} binary;
const char *payload;
unsigned int dstaddr;
unsigned int execaddr;
unsigned int nandblksz;
unsigned int nandbadblklocation;
unsigned int nandeccmode;
unsigned int nandpagesz;
struct ext_hdr_v0_reg regdata;
};
};
#define IMAGE_CFG_ELEMENT_MAX 256
/*
* Byte 8 of the image header contains the version number. In the v0
* header, byte 8 was reserved, and always set to 0. In the v1 header,
* byte 8 has been changed to a proper field, set to 1.
*/
static unsigned int image_version(void *header)
{
unsigned char *ptr = header;
return ptr[8];
}
/*
* Utility functions to manipulate boot mode and ecc modes (convert
* them back and forth between description strings and the
* corresponding numerical identifiers).
*/
static const char *image_boot_mode_name(unsigned int id)
{
int i;
for (i = 0; boot_modes[i].name; i++)
if (boot_modes[i].id == id)
return boot_modes[i].name;
return NULL;
}
int image_boot_mode_id(const char *boot_mode_name)
{
int i;
for (i = 0; boot_modes[i].name; i++)
if (!strcmp(boot_modes[i].name, boot_mode_name))
return boot_modes[i].id;
return -1;
}
static const char *image_nand_ecc_mode_name(unsigned int id)
{
int i;
for (i = 0; nand_ecc_modes[i].name; i++)
if (nand_ecc_modes[i].id == id)
return nand_ecc_modes[i].name;
return NULL;
}
int image_nand_ecc_mode_id(const char *nand_ecc_mode_name)
{
int i;
for (i = 0; nand_ecc_modes[i].name; i++)
if (!strcmp(nand_ecc_modes[i].name, nand_ecc_mode_name))
return nand_ecc_modes[i].id;
return -1;
}
static struct image_cfg_element *
image_find_option(struct image_cfg_element *image_cfg,
int cfgn, unsigned int optiontype)
{
int i;
for (i = 0; i < cfgn; i++) {
if (image_cfg[i].type == optiontype)
return &image_cfg[i];
}
return NULL;
}
static unsigned int
image_count_options(struct image_cfg_element *image_cfg,
int cfgn, unsigned int optiontype)
{
int i;
unsigned int count = 0;
for (i = 0; i < cfgn; i++)
if (image_cfg[i].type == optiontype)
count++;
return count;
}
/*
* Compute a 8-bit checksum of a memory area. This algorithm follows
* the requirements of the Marvell SoC BootROM specifications.
*/
static uint8_t image_checksum8(void *start, uint32_t len)
{
uint8_t csum = 0;
uint8_t *p = start;
/* check len and return zero checksum if invalid */
if (!len)
return 0;
do {
csum += *p;
p++;
} while (--len);
return csum;
}
static uint32_t image_checksum32 (void *start, uint32_t len)
{
uint32_t csum = 0;
uint32_t *p = start;
/* check len and return zero checksum if invalid */
if (!len)
return 0;
if (len % sizeof(uint32_t)) {
fprintf (stderr, "Length %d is not in multiple of %zu\n",
len, sizeof(uint32_t));
return 0;
}
do {
csum += *p;
p++;
len -= sizeof(uint32_t);
} while (len > 0);
return csum;
}
static void usage(const char *prog)
{
printf("Usage: %s [-c | -x] -i <input> -o <output>\n", prog);
printf(" -c: create a new image\n");
printf(" -x: extract an existing image\n");
printf(" -i: input file\n");
printf(" when used with -c, should point to a kwbimage.cfg file\n");
printf(" when used with -x, should point to the image to be extracted\n");
printf(" -o: output file/directory\n");
printf(" when used with -c, should point to the image file to create\n");
printf(" when used with -x, should point to a directory when the image will be extracted\n");
printf(" -v: verbose\n");
printf(" -h: this help text\n");
printf(" Options specific to image creation:\n");
printf(" -p: path to payload image. Overrides the PAYLOAD line from kwbimage.cfg\n");
printf(" -m: boot media. Overrides the BOOT_FROM line from kwbimage.cfg\n");
printf(" -d: load address. Overrides the DEST_ADDR line from kwbimage.cfg\n");
printf(" -e: exec address. Overrides the EXEC_ADDR line from kwbimage.cfg\n");
}
static int image_extract_payload(void *payload, size_t sz, const char *output)
{
char *imageoutname;
FILE *imageout;
int ret;
ret = asprintf(&imageoutname, "%s/payload", output);
if (ret < 0) {
fprintf(stderr, "Cannot allocate memory\n");
return -1;
}
imageout = fopen(imageoutname, "w+");
if (!imageout) {
fprintf(stderr, "Could not open output file %s\n",
imageoutname);
free(imageoutname);
return -1;
}
ret = fwrite(payload, sz, 1, imageout);
if (ret != 1) {
fprintf(stderr, "Could not write to open file %s\n",
imageoutname);
fclose(imageout);
free(imageoutname);
return -1;
}
fclose(imageout);
free(imageoutname);
return 0;
}
static int image_extract_v0(void *fdimap, const char *output, FILE *focfg)
{
struct main_hdr_v0 *main_hdr = fdimap;
struct ext_hdr_v0 *ext_hdr;
const char *boot_mode_name;
uint32_t *img_checksum;
size_t payloadsz;
int cksum, i;
/*
* Verify checksum. When calculating the header, discard the
* last byte of the header, which itself contains the
* checksum.
*/
cksum = image_checksum8(main_hdr, sizeof(struct main_hdr_v0)-1);
if (cksum != main_hdr->checksum) {
fprintf(stderr,
"Invalid main header checksum: 0x%08x vs. 0x%08x\n",
cksum, main_hdr->checksum);
return -1;
}
boot_mode_name = image_boot_mode_name(main_hdr->blockid);
if (!boot_mode_name) {
fprintf(stderr, "Invalid boot ID: 0x%x\n",
main_hdr->blockid);
return -1;
}
fprintf(focfg, "VERSION 0\n");
fprintf(focfg, "BOOT_FROM %s\n", boot_mode_name);
fprintf(focfg, "DESTADDR %08x\n", main_hdr->destaddr);
fprintf(focfg, "EXECADDR %08x\n", main_hdr->execaddr);
if (!strcmp(boot_mode_name, "nand")) {
const char *nand_ecc_mode =
image_nand_ecc_mode_name(main_hdr->nandeccmode);
fprintf(focfg, "NAND_ECCMODE %s\n",
nand_ecc_mode);
fprintf(focfg, "NAND_PAGESZ %08x\n",
main_hdr->nandpagesize);
}
/* No extension header, we're done */
if (!main_hdr->ext)
return 0;
ext_hdr = fdimap + sizeof(struct main_hdr_v0);
for (i = 0; i < EXT_HDR_V0_REG_COUNT; i++) {
if (ext_hdr->rcfg[i].raddr == 0 &&
ext_hdr->rcfg[i].rdata == 0)
break;
fprintf(focfg, "DATA %08x %08x\n",
ext_hdr->rcfg[i].raddr,
ext_hdr->rcfg[i].rdata);
}
/* The image is concatenated with a 32 bits checksum */
payloadsz = main_hdr->blocksize - sizeof(uint32_t);
img_checksum = (uint32_t *) (fdimap + main_hdr->srcaddr + payloadsz);
if (*img_checksum != image_checksum32(fdimap + main_hdr->srcaddr,
payloadsz)) {
fprintf(stderr, "The image checksum does not match\n");
return -1;
}
/* Finally, handle the image itself */
fprintf(focfg, "PAYLOAD %s/payload\n", output);
return image_extract_payload(fdimap + main_hdr->srcaddr,
payloadsz, output);
}
static int image_extract_binary_hdr_v1(const void *binary, const char *output,
FILE *focfg, int hdrnum, size_t binsz)
{
char *binaryoutname;
FILE *binaryout;
const unsigned int *args;
unsigned int nargs;
int ret, i;
args = binary;
nargs = args[0];
args++;
ret = asprintf(&binaryoutname, "%s/binary.%d", output,
hdrnum);
if (ret < 0) {
fprintf(stderr, "Couldn't not allocate memory\n");
return ret;
}
binaryout = fopen(binaryoutname, "w+");
if (!binaryout) {
fprintf(stderr, "Couldn't open output file %s\n",
binaryoutname);
free(binaryoutname);
return -1;
}
ret = fwrite(binary + (nargs + 1) * sizeof(unsigned int),
binsz - (nargs + 1) * sizeof(unsigned int), 1,
binaryout);
if (ret != 1) {
fprintf(stderr, "Could not write to output file %s\n",
binaryoutname);
fclose(binaryout);
free(binaryoutname);
return -1;
}
fclose(binaryout);
fprintf(focfg, "BINARY %s", binaryoutname);
for (i = 0; i < nargs; i++)
fprintf(focfg, " %08x", args[i]);
fprintf(focfg, "\n");
free(binaryoutname);
return 0;
}
static int image_extract_v1(void *fdimap, const char *output, FILE *focfg)
{
struct main_hdr_v1 *main_hdr = fdimap;
struct opt_hdr_v1 *opt_hdr;
const char *boot_mode_name;
int headersz = KWBHEADER_V1_SIZE(main_hdr);
int hasheaders;
uint8_t cksum;
int opthdrid;
/*
* Verify the checkum. We have to substract the checksum
* itself, because when the checksum is calculated, the
* checksum field is 0.
*/
cksum = image_checksum8(main_hdr, headersz);
cksum -= main_hdr->checksum;
if (cksum != main_hdr->checksum) {
fprintf(stderr,
"Invalid main header checksum: 0x%08x vs. 0x%08x\n",
cksum, main_hdr->checksum);
return -1;
}
/* First, take care of the main header */
boot_mode_name = image_boot_mode_name(main_hdr->blockid);
if (!boot_mode_name) {
fprintf(stderr, "Invalid boot ID: 0x%x\n",
main_hdr->blockid);
return -1;
}
fprintf(focfg, "VERSION 1\n");
fprintf(focfg, "BOOT_FROM %s\n", boot_mode_name);
fprintf(focfg, "DESTADDR %08x\n", main_hdr->destaddr);
fprintf(focfg, "EXECADDR %08x\n", main_hdr->execaddr);
fprintf(focfg, "NAND_BLKSZ %08x\n",
main_hdr->nandblocksize * 64 * 1024);
fprintf(focfg, "NAND_BADBLK_LOCATION %02x\n",
main_hdr->nandbadblklocation);
hasheaders = main_hdr->ext;
opt_hdr = fdimap + sizeof(struct main_hdr_v1);
opthdrid = 0;
/* Then, go through all the extension headers */
while (hasheaders) {
int opthdrsz = KWBHEADER_V1_SIZE(opt_hdr);
switch (opt_hdr->headertype) {
case OPT_HDR_V1_BINARY_TYPE:
image_extract_binary_hdr_v1(opt_hdr->data, output,
focfg, opthdrid,
opthdrsz -
sizeof(struct opt_hdr_v1));
break;
case OPT_HDR_V1_SECURE_TYPE:
case OPT_HDR_V1_REGISTER_TYPE:
fprintf(stderr,
"Support for header type 0x%x not implemented\n",
opt_hdr->headertype);
exit(1);
break;
default:
fprintf(stderr, "Invalid header type 0x%x\n",
opt_hdr->headertype);
exit(1);
}
/*
* The first byte of the last double word of the
* current header indicates whether there is a next
* header or not.
*/
hasheaders = ((char *)opt_hdr)[opthdrsz - 4];
/* Move to the next header */
opt_hdr = ((void *)opt_hdr) + opthdrsz;
opthdrid++;
}
/* Finally, handle the image itself */
fprintf(focfg, "PAYLOAD %s/payload\n", output);
return image_extract_payload(fdimap + main_hdr->srcaddr,
main_hdr->blocksize - 4,
output);
}
static int image_extract(const char *input, const char *output)
{
int fdi, ret;
struct stat fdistat, fdostat;
void *fdimap;
char *focfgname;
FILE *focfg;
fdi = open(input, O_RDONLY);
if (fdi < 0) {
fprintf(stderr, "Cannot open input file %s: %m\n",
input);
return -1;
}
ret = fstat(fdi, &fdistat);
if (ret < 0) {
fprintf(stderr, "Cannot stat input file %s: %m\n",
input);
close(fdi);
return -1;
}
fdimap = mmap(NULL, fdistat.st_size, PROT_READ, MAP_PRIVATE, fdi, 0);
if (fdimap == MAP_FAILED) {
fprintf(stderr, "Cannot map input file %s: %m\n",
input);
close(fdi);
return -1;
}
close(fdi);
ret = stat(output, &fdostat);
if (ret < 0) {
fprintf(stderr, "Cannot stat output directory %s: %m\n",
output);
munmap(fdimap, fdistat.st_size);
return -1;
}
if (!S_ISDIR(fdostat.st_mode)) {
fprintf(stderr, "Output %s should be a directory\n",
output);
munmap(fdimap, fdistat.st_size);
return -1;
}
ret = asprintf(&focfgname, "%s/kwbimage.cfg", output);
if (ret < 0) {
fprintf(stderr, "Failed to allocate memory\n");
munmap(fdimap, fdistat.st_size);
return -1;
}
focfg = fopen(focfgname, "w+");
if (!focfg) {
fprintf(stderr, "Output file %s could not be created\n",
focfgname);
free(focfgname);
munmap(fdimap, fdistat.st_size);
return -1;
}
free(focfgname);
if (image_version(fdimap) == 0)
ret = image_extract_v0(fdimap, output, focfg);
else if (image_version(fdimap) == 1)
ret = image_extract_v1(fdimap, output, focfg);
else {
fprintf(stderr, "Invalid image version %d\n",
image_version(fdimap));
ret = -1;
}
fclose(focfg);
munmap(fdimap, fdistat.st_size);
return ret;
}
static int image_create_payload(void *payload_start, size_t payloadsz,
const char *payload_filename)
{
FILE *payload;
struct stat s;
uint32_t *payload_checksum =
(uint32_t *) (payload_start + payloadsz);
int ret;
payload = fopen(payload_filename, "r");
if (!payload) {
fprintf(stderr, "Cannot open payload file %s\n",
payload_filename);
return -1;
}
ret = stat(payload_filename, &s);
if (ret < 0) {
fprintf(stderr, "Cannot stat payload file %s\n",
payload_filename);
fclose(payload);
return ret;
}
ret = fread(payload_start, s.st_size, 1, payload);
fclose(payload);
if (ret != 1) {
fprintf(stderr, "Cannot read payload file %s\n",
payload_filename);
return -1;
}
*payload_checksum = image_checksum32(payload_start, payloadsz);
return 0;
}
static void *image_create_v0(struct image_cfg_element *image_cfg,
int cfgn, const char *output, size_t *imagesz)
{
struct image_cfg_element *e, *payloade;
size_t headersz, payloadsz, totalsz;
struct main_hdr_v0 *main_hdr;
struct ext_hdr_v0 *ext_hdr;
void *image;
int has_ext = 0;
int ret;
/* Calculate the size of the header and the size of the
* payload */
headersz = sizeof(struct main_hdr_v0);
payloadsz = 0;
if (image_count_options(image_cfg, cfgn, IMAGE_CFG_DATA) > 0) {
has_ext = 1;
headersz += sizeof(struct ext_hdr_v0);
}
if (image_count_options(image_cfg, cfgn, IMAGE_CFG_PAYLOAD) > 1) {
fprintf(stderr, "More than one payload, not possible\n");
return NULL;
}
payloade = image_find_option(image_cfg, cfgn, IMAGE_CFG_PAYLOAD);
if (payloade) {
struct stat s;
int ret;
ret = stat(payloade->payload, &s);
if (ret < 0) {
fprintf(stderr, "Cannot stat payload file %s\n",
payloade->payload);
return NULL;
}
/* payload size must be multiple of 32b */
payloadsz = 4 * ((s.st_size + 3)/4);
}
/* Headers, payload and 32-bits checksum */
totalsz = headersz + payloadsz + sizeof(uint32_t);
image = malloc(totalsz);
if (!image) {
fprintf(stderr, "Cannot allocate memory for image\n");
return NULL;
}
memset(image, 0, totalsz);
main_hdr = image;
/* Fill in the main header */
main_hdr->blocksize = payloadsz + sizeof(uint32_t);
main_hdr->srcaddr = headersz;
main_hdr->ext = has_ext;
e = image_find_option(image_cfg, cfgn, IMAGE_CFG_BOOT_FROM);
if (e)
main_hdr->blockid = e->bootfrom;
e = image_find_option(image_cfg, cfgn, IMAGE_CFG_DEST_ADDR);
if (e)
main_hdr->destaddr = e->dstaddr;
e = image_find_option(image_cfg, cfgn, IMAGE_CFG_EXEC_ADDR);
if (e)
main_hdr->execaddr = e->execaddr;
e = image_find_option(image_cfg, cfgn, IMAGE_CFG_NAND_ECC_MODE);
if (e)
main_hdr->nandeccmode = e->nandeccmode;
e = image_find_option(image_cfg, cfgn, IMAGE_CFG_NAND_PAGESZ);
if (e)
main_hdr->nandpagesize = e->nandpagesz;
main_hdr->checksum = image_checksum8(image,
sizeof(struct main_hdr_v0));
/* Generate the ext header */
if (has_ext) {
int cfgi, datai;
ext_hdr = image + sizeof(struct main_hdr_v0);
ext_hdr->offset = 0x40;
for (cfgi = 0, datai = 0; cfgi < cfgn; cfgi++) {
e = &image_cfg[cfgi];
if (e->type != IMAGE_CFG_DATA)
continue;
ext_hdr->rcfg[datai].raddr = e->regdata.raddr;
ext_hdr->rcfg[datai].rdata = e->regdata.rdata;
datai++;
}
ext_hdr->checksum = image_checksum8(ext_hdr,
sizeof(struct ext_hdr_v0));
}
if (payloade) {
ret = image_create_payload(image + headersz, payloadsz,
payloade->payload);
if (ret < 0)
return NULL;
}
*imagesz = totalsz;
return image;
}
static void *image_create_v1(struct image_cfg_element *image_cfg,
int cfgn, const char *output, size_t *imagesz)
{
struct image_cfg_element *e, *payloade, *binarye;
struct main_hdr_v1 *main_hdr;
size_t headersz, payloadsz, totalsz;
void *image, *cur;
int hasext = 0;
int ret;
/* Calculate the size of the header and the size of the
* payload */
headersz = sizeof(struct main_hdr_v1);
payloadsz = 0;
if (image_count_options(image_cfg, cfgn, IMAGE_CFG_BINARY) > 1) {
fprintf(stderr, "More than one binary blob, not supported\n");
return NULL;
}
if (image_count_options(image_cfg, cfgn, IMAGE_CFG_PAYLOAD) > 1) {
fprintf(stderr, "More than one payload, not possible\n");
return NULL;
}
binarye = image_find_option(image_cfg, cfgn, IMAGE_CFG_BINARY);
if (binarye) {
struct stat s;
ret = stat(binarye->binary.file, &s);
if (ret < 0) {
char *cwd = get_current_dir_name();
fprintf(stderr,
"Didn't find the file '%s' in '%s' which is mandatory to generate the image\n"
"This file generally contains the DDR3 training code, and should be extracted from an existing bootable\n"
"image for your board. See 'kwbimage -x' to extract it from an existing image.\n",
binarye->binary.file, cwd);
free(cwd);
return NULL;
}
headersz += s.st_size +
binarye->binary.nargs * sizeof(unsigned int);
hasext = 1;
}
payloade = image_find_option(image_cfg, cfgn, IMAGE_CFG_PAYLOAD);
if (payloade) {
struct stat s;
ret = stat(payloade->payload, &s);
if (ret < 0) {
fprintf(stderr, "Cannot stat payload file %s\n",
payloade->payload);
return NULL;
}
/* payload size must be multiple of 32b */
payloadsz = 4 * ((s.st_size + 3)/4);
}
/* The payload should be aligned on some reasonable
* boundary */
headersz = ALIGN_SUP(headersz, 4096);
/* The total size includes the headers, the payload, and the
* 32 bits checksum at the end of the payload */
totalsz = headersz + payloadsz + sizeof(uint32_t);
image = malloc(totalsz);
if (!image) {
fprintf(stderr, "Cannot allocate memory for image\n");
return NULL;
}
memset(image, 0, totalsz);
cur = main_hdr = image;
cur += sizeof(struct main_hdr_v1);
/* Fill the main header */
main_hdr->blocksize = payloadsz + sizeof(uint32_t);
main_hdr->headersz_lsb = headersz & 0xFFFF;
main_hdr->headersz_msb = (headersz & 0xFFFF0000) >> 16;
main_hdr->srcaddr = headersz;
main_hdr->ext = hasext;
main_hdr->version = 1;
e = image_find_option(image_cfg, cfgn, IMAGE_CFG_BOOT_FROM);
if (e)
main_hdr->blockid = e->bootfrom;
e = image_find_option(image_cfg, cfgn, IMAGE_CFG_DEST_ADDR);
if (e)
main_hdr->destaddr = e->dstaddr;
e = image_find_option(image_cfg, cfgn, IMAGE_CFG_EXEC_ADDR);
if (e)
main_hdr->execaddr = e->execaddr;
e = image_find_option(image_cfg, cfgn, IMAGE_CFG_NAND_BLKSZ);
if (e)
main_hdr->nandblocksize = e->nandblksz / (64 * 1024);
e = image_find_option(image_cfg, cfgn, IMAGE_CFG_NAND_BADBLK_LOCATION);
if (e)
main_hdr->nandbadblklocation = e->nandbadblklocation;
if (binarye) {
struct opt_hdr_v1 *hdr = cur;
unsigned int *args;
size_t binhdrsz;
struct stat s;
int argi;
FILE *bin;
hdr->headertype = OPT_HDR_V1_BINARY_TYPE;
bin = fopen(binarye->binary.file, "r");
if (!bin) {
fprintf(stderr, "Cannot open binary file %s\n",
binarye->binary.file);
return NULL;
}
fstat(fileno(bin), &s);
binhdrsz = sizeof(struct opt_hdr_v1) +
(binarye->binary.nargs + 1) * sizeof(unsigned int) +
s.st_size;
hdr->headersz_lsb = binhdrsz & 0xFFFF;
hdr->headersz_msb = (binhdrsz & 0xFFFF0000) >> 16;
cur += sizeof(struct opt_hdr_v1);
args = cur;
*args = binarye->binary.nargs;
args++;
for (argi = 0; argi < binarye->binary.nargs; argi++)
args[argi] = binarye->binary.args[argi];
cur += (binarye->binary.nargs + 1) * sizeof(unsigned int);
ret = fread(cur, s.st_size, 1, bin);
if (ret != 1) {
fprintf(stderr,
"Could not read binary image %s\n",
binarye->binary.file);
return NULL;
}
fclose(bin);
cur += s.st_size;
/*
* For now, we don't support more than one binary
* header, and no other header types are
* supported. So, the binary header is necessarily the
* last one
*/
*((unsigned char *) cur) = 0;
cur += sizeof(uint32_t);
}
/* Calculate and set the header checksum */
main_hdr->checksum = image_checksum8(main_hdr, headersz);
if (payloade) {
ret = image_create_payload(image + headersz, payloadsz,
payloade->payload);
if (ret < 0)
return NULL;
}
*imagesz = totalsz;
return image;
}
static int image_create_config_parse_oneline(char *line,
struct image_cfg_element *el)
{
char *keyword, *saveptr;
keyword = strtok_r(line, " ", &saveptr);
if (!strcmp(keyword, "VERSION")) {
char *value = strtok_r(NULL, " ", &saveptr);
el->type = IMAGE_CFG_VERSION;
el->version = atoi(value);
} else if (!strcmp(keyword, "BOOT_FROM")) {
char *value = strtok_r(NULL, " ", &saveptr);
el->type = IMAGE_CFG_BOOT_FROM;
el->bootfrom = image_boot_mode_id(value);
if (el->bootfrom < 0) {
fprintf(stderr,
"Invalid boot media '%s'\n", value);
return -1;
}
} else if (!strcmp(keyword, "DESTADDR")) {
char *value = strtok_r(NULL, " ", &saveptr);
el->type = IMAGE_CFG_DEST_ADDR;
el->dstaddr = strtoul(value, NULL, 16);
} else if (!strcmp(keyword, "EXECADDR")) {
char *value = strtok_r(NULL, " ", &saveptr);
el->type = IMAGE_CFG_EXEC_ADDR;
el->execaddr = strtoul(value, NULL, 16);
} else if (!strcmp(keyword, "NAND_BLKSZ")) {
char *value = strtok_r(NULL, " ", &saveptr);
el->type = IMAGE_CFG_NAND_BLKSZ;
el->nandblksz = strtoul(value, NULL, 16);
} else if (!strcmp(keyword, "NAND_BADBLK_LOCATION")) {
char *value = strtok_r(NULL, " ", &saveptr);
el->type = IMAGE_CFG_NAND_BADBLK_LOCATION;
el->nandbadblklocation =
strtoul(value, NULL, 16);
} else if (!strcmp(keyword, "NAND_ECCMODE")) {
char *value = strtok_r(NULL, " ", &saveptr);
el->type = IMAGE_CFG_NAND_ECC_MODE;
el->nandeccmode = image_nand_ecc_mode_id(value);
if (el->nandeccmode < 0) {
fprintf(stderr,
"Invalid NAND ECC mode '%s'\n", value);
return -1;
}
} else if (!strcmp(keyword, "NAND_PAGESZ")) {
char *value = strtok_r(NULL, " ", &saveptr);
el->type = IMAGE_CFG_NAND_PAGESZ;
el->nandpagesz = strtoul(value, NULL, 16);
} else if (!strcmp(keyword, "BINARY")) {
char *value = strtok_r(NULL, " ", &saveptr);
int argi = 0;
el->type = IMAGE_CFG_BINARY;
el->binary.file = strdup(value);
while (1) {
value = strtok_r(NULL, " ", &saveptr);
if (!value)
break;
el->binary.args[argi] = strtoul(value, NULL, 16);
argi++;
if (argi >= BINARY_MAX_ARGS) {
fprintf(stderr,
"Too many argument for binary\n");
return -1;
}
}
el->binary.nargs = argi;
} else if (!strcmp(keyword, "DATA")) {
char *value1 = strtok_r(NULL, " ", &saveptr);
char *value2 = strtok_r(NULL, " ", &saveptr);
if (!value1 || !value2) {
fprintf(stderr, "Invalid number of arguments for DATA\n");
return -1;
}
el->type = IMAGE_CFG_DATA;
el->regdata.raddr = strtoul(value1, NULL, 16);
el->regdata.rdata = strtoul(value2, NULL, 16);
} else if (!strcmp(keyword, "PAYLOAD")) {
char *value = strtok_r(NULL, " ", &saveptr);
el->type = IMAGE_CFG_PAYLOAD;
el->payload = strdup(value);
} else {
fprintf(stderr, "Ignoring unknown line '%s'\n", line);
}
return 0;
}
/*
* Parse the configuration file 'fcfg' into the array of configuration
* elements 'image_cfg', and return the number of configuration
* elements in 'cfgn'.
*/
static int image_create_config_parse(FILE *fcfg,
struct image_cfg_element *image_cfg,
int *cfgn)
{
int ret;
int cfgi = 0;
/* Parse the configuration file */
while (!feof(fcfg)) {
char *line;
char buf[256];
/* Read the current line */
memset(buf, 0, sizeof(buf));
line = fgets(buf, sizeof(buf), fcfg);
if (!line)
break;
/* Ignore useless lines */
if (line[0] == '\n' || line[0] == '#')
continue;
/* Strip final newline */
if (line[strlen(line) - 1] == '\n')
line[strlen(line) - 1] = 0;
/* Parse the current line */
ret = image_create_config_parse_oneline(line,
&image_cfg[cfgi]);
if (ret)
return ret;
cfgi++;
if (cfgi >= IMAGE_CFG_ELEMENT_MAX) {
fprintf(stderr, "Too many configuration elements in .cfg file\n");
return -1;
}
}
*cfgn = cfgi;
return 0;
}
static int image_override_payload(struct image_cfg_element *image_cfg,
int *cfgn, const char *payload)
{
struct image_cfg_element *e;
int cfgi = *cfgn;
if (!payload)
return 0;
e = image_find_option(image_cfg, *cfgn, IMAGE_CFG_PAYLOAD);
if (e) {
e->payload = payload;
return 0;
}
image_cfg[cfgi].type = IMAGE_CFG_PAYLOAD;
image_cfg[cfgi].payload = payload;
cfgi++;
*cfgn = cfgi;
return 0;
}
static int image_override_bootmedia(struct image_cfg_element *image_cfg,
int *cfgn, const char *bootmedia)
{
struct image_cfg_element *e;
int bootfrom;
int cfgi = *cfgn;
if (!bootmedia)
return 0;
bootfrom = image_boot_mode_id(bootmedia);
if (!bootfrom) {
fprintf(stderr,
"Invalid boot media '%s'\n", bootmedia);
return -1;
}
e = image_find_option(image_cfg, *cfgn, IMAGE_CFG_BOOT_FROM);
if (e) {
e->bootfrom = bootfrom;
return 0;
}
image_cfg[cfgi].type = IMAGE_CFG_BOOT_FROM;
image_cfg[cfgi].bootfrom = bootfrom;
cfgi++;
*cfgn = cfgi;
return 0;
}
static int image_override_dstaddr(struct image_cfg_element *image_cfg,
int *cfgn, uint32_t dstaddr)
{
struct image_cfg_element *e;
int cfgi = *cfgn;
if (dstaddr == ADDR_INVALID)
return 0;
e = image_find_option(image_cfg, *cfgn, IMAGE_CFG_DEST_ADDR);
if (e) {
e->dstaddr = dstaddr;
return 0;
}
image_cfg[cfgi].type = IMAGE_CFG_DEST_ADDR;
image_cfg[cfgi].dstaddr = dstaddr;
cfgi++;
*cfgn = cfgi;
return 0;
}
static int image_override_execaddr(struct image_cfg_element *image_cfg,
int *cfgn, uint32_t execaddr)
{
struct image_cfg_element *e;
int cfgi = *cfgn;
if (execaddr == ADDR_INVALID)
return 0;
e = image_find_option(image_cfg, *cfgn, IMAGE_CFG_EXEC_ADDR);
if (e) {
e->execaddr = execaddr;
return 0;
}
image_cfg[cfgi].type = IMAGE_CFG_EXEC_ADDR;
image_cfg[cfgi].execaddr = execaddr;
cfgi++;
*cfgn = cfgi;
return 0;
}
static int image_get_version(struct image_cfg_element *image_cfg,
int cfgn)
{
struct image_cfg_element *e;
e = image_find_option(image_cfg, cfgn, IMAGE_CFG_VERSION);
if (!e)
return -1;
return e->version;
}
static void image_dump_config(struct image_cfg_element *image_cfg,
int cfgn)
{
int cfgi;
printf("== configuration dump\n");
for (cfgi = 0; cfgi < cfgn; cfgi++) {
struct image_cfg_element *e = &image_cfg[cfgi];
switch (e->type) {
case IMAGE_CFG_VERSION:
printf("VERSION %u\n", e->version);
break;
case IMAGE_CFG_BOOT_FROM:
printf("BOOTFROM %s\n",
image_boot_mode_name(e->bootfrom));
break;
case IMAGE_CFG_DEST_ADDR:
printf("DESTADDR 0x%x\n", e->dstaddr);
break;
case IMAGE_CFG_EXEC_ADDR:
printf("EXECADDR 0x%x\n", e->execaddr);
break;
case IMAGE_CFG_NAND_BLKSZ:
printf("NANDBLKSZ 0x%x\n", e->nandblksz);
break;
case IMAGE_CFG_NAND_BADBLK_LOCATION:
printf("NANDBADBLK 0x%x\n", e->nandbadblklocation);
break;
case IMAGE_CFG_NAND_ECC_MODE:
printf("NAND_ECCMODE 0x%x\n", e->nandeccmode);
break;
case IMAGE_CFG_NAND_PAGESZ:
printf("NAND_PAGESZ 0x%x\n", e->nandpagesz);
break;
case IMAGE_CFG_BINARY:
printf("BINARY %s (%d args)\n", e->binary.file,
e->binary.nargs);
break;
case IMAGE_CFG_PAYLOAD:
printf("PAYLOAD %s\n", e->payload);
break;
case IMAGE_CFG_DATA:
printf("DATA 0x%x 0x%x\n",
e->regdata.raddr,
e->regdata.rdata);
break;
default:
printf("Error, unknown type\n");
break;
}
}
printf("== end configuration dump\n");
}
static int image_create(const char *input, const char *output,
const char *payload, const char *bootmedia,
uint32_t dstaddr, uint32_t execaddr,
int verbose)
{
struct image_cfg_element *image_cfg;
FILE *fcfg, *outputimg;
void *image = NULL;
int version;
size_t imagesz;
int cfgn;
int ret;
fcfg = fopen(input, "r");
if (!fcfg) {
fprintf(stderr, "Could not open input file %s\n",
input);
return -1;
}
image_cfg = malloc(IMAGE_CFG_ELEMENT_MAX *
sizeof(struct image_cfg_element));
if (!image_cfg) {
fprintf(stderr, "Cannot allocate memory\n");
fclose(fcfg);
return -1;
}
memset(image_cfg, 0,
IMAGE_CFG_ELEMENT_MAX * sizeof(struct image_cfg_element));
rewind(fcfg);
ret = image_create_config_parse(fcfg, image_cfg, &cfgn);
fclose(fcfg);
if (ret) {
free(image_cfg);
return -1;
}
image_override_payload(image_cfg, &cfgn, payload);
image_override_bootmedia(image_cfg, &cfgn, bootmedia);
image_override_dstaddr(image_cfg, &cfgn, dstaddr);
image_override_execaddr(image_cfg, &cfgn, execaddr);
if (!image_find_option(image_cfg, cfgn, IMAGE_CFG_BOOT_FROM) ||
!image_find_option(image_cfg, cfgn, IMAGE_CFG_DEST_ADDR) ||
!image_find_option(image_cfg, cfgn, IMAGE_CFG_EXEC_ADDR)) {
fprintf(stderr,
"Missing information (either boot media, exec addr or dest addr)\n");
free(image_cfg);
return -1;
}
if (verbose)
image_dump_config(image_cfg, cfgn);
version = image_get_version(image_cfg, cfgn);
if (version == 0)
image = image_create_v0(image_cfg, cfgn, output, &imagesz);
else if (version == 1)
image = image_create_v1(image_cfg, cfgn, output, &imagesz);
else if (version == -1) {
fprintf(stderr, "File %s does not have the VERSION field\n",
input);
free(image_cfg);
return -1;
}
if (!image) {
fprintf(stderr, "Could not create image\n");
free(image_cfg);
return -1;
}
free(image_cfg);
outputimg = fopen(output, "w");
if (!outputimg) {
fprintf(stderr, "Cannot open output image %s for writing\n",
output);
free(image);
return -1;
}
ret = fwrite(image, imagesz, 1, outputimg);
if (ret != 1) {
fprintf(stderr, "Cannot write to output image %s\n",
output);
fclose(outputimg);
free(image);
return -1;
}
fclose(outputimg);
free(image);
return 0;
}
enum {
ACTION_CREATE,
ACTION_EXTRACT,
ACTION_DUMP,
ACTION_HELP,
};
int main(int argc, char *argv[])
{
int action = -1, opt, verbose = 0;
const char *input = NULL, *output = NULL,
*payload = NULL, *bootmedia = NULL;
uint32_t execaddr = ADDR_INVALID, dstaddr = ADDR_INVALID;
while ((opt = getopt(argc, argv, "hxci:o:p:m:e:d:v")) != -1) {
switch (opt) {
case 'x':
action = ACTION_EXTRACT;
break;
case 'c':
action = ACTION_CREATE;
break;
case 'i':
input = optarg;
break;
case 'o':
output = optarg;
break;
case 'p':
payload = optarg;
break;
case 'm':
bootmedia = optarg;
break;
case 'e':
execaddr = strtol(optarg, NULL, 0);
break;
case 'd':
dstaddr = strtol(optarg, NULL, 0);
break;
case 'v':
verbose = 1;
break;
case 'h':
action = ACTION_HELP;
break;
}
}
/* We should have consumed all arguments */
if (optind != argc) {
usage(argv[0]);
exit(1);
}
if (action != ACTION_HELP && !input) {
fprintf(stderr, "Missing input file\n");
usage(argv[0]);
exit(1);
}
if ((action == ACTION_EXTRACT || action == ACTION_CREATE) &&
!output) {
fprintf(stderr, "Missing output file\n");
usage(argv[0]);
exit(1);
}
if (action == ACTION_EXTRACT &&
(bootmedia || payload ||
(execaddr != ADDR_INVALID) || (dstaddr != ADDR_INVALID))) {
fprintf(stderr,
"-m, -p, -e or -d do not make sense when extracting an image");
usage(argv[0]);
exit(1);
}
switch (action) {
case ACTION_EXTRACT:
return image_extract(input, output);
case ACTION_CREATE:
return image_create(input, output, payload,
bootmedia, dstaddr, execaddr,
verbose);
case ACTION_HELP:
usage(argv[0]);
return 0;
default:
fprintf(stderr, "No action specified\n");
usage(argv[0]);
exit(1);
}
return 0;
}