Skip to content

Instantly share code, notes, and snippets.

@simias
Created Feb 1, 2017
Embed
What would you like to do?
Rewrite of barebox's mkpimage
/* Replacement for altera's proprietary mkpimage (part of their EDS)
*
* Create an Altera BootROM-compatible image of the Second Stage Boot
* Loader (SSBL).
*
* This program should generate the same output as Altera's mkpimage
* version 16.1 (build 196). If it doesn't it's a bug.
*
* The original version of this program was part of barebox.
*
* Distributed under the GNU GPL v2.
*/
#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#define MKPIMAGE_VERSION "1.0"
#define VALIDATION_WORD 0x31305341
struct socfpga_header_v0 {
uint8_t validation_word[4];
uint8_t version;
uint8_t flags;
uint8_t program_length[2];
uint8_t spare[2];
uint8_t checksum[2];
};
struct socfpga_header_v1 {
uint8_t validation_word[4];
uint8_t version;
uint8_t flags;
uint8_t header_length[2];
uint8_t program_length[4];
uint8_t program_offset[4];
uint8_t spare[2];
uint8_t checksum[2];
};
static uint32_t bb_header[] = {
0xea00007e, /* b 0x200 */
0xeafffffe, /* 1: b 1b */
0xeafffffe, /* 1: b 1b */
0xeafffffe, /* 1: b 1b */
0xeafffffe, /* 1: b 1b */
0xeafffffe, /* 1: b 1b */
0xeafffffe, /* 1: b 1b */
0xeafffffe, /* 1: b 1b */
0x65726162, /* 'bare' */
0x00786f62, /* 'box\0' */
0x00000000, /* padding */
0x00000000, /* padding */
0x00000000, /* padding */
0x00000000, /* padding */
0x00000000, /* padding */
0x00000000, /* padding */
0x00000000, /* socfpga header */
0x00000000, /* socfpga header */
0x00000000, /* socfpga header */
0xea00006b, /* entry. b 0x200 */
};
static int add_file(int outfd,
const char *path,
unsigned header_version,
size_t alignment,
int barebox,
unsigned long entry_offset);
static void add_socfpga_header_v0(uint8_t *buf, size_t size);
static void add_socfpga_header_v1(uint8_t *buf,
size_t size,
unsigned long entry_offset);
static uint32_t crc32(uint8_t *buf, size_t length);
static int write_all(int fd, uint8_t *buf, size_t size);
static void usage(const char *bin) {
fprintf(stderr,
"Buildroot mkpimage v" MKPIMAGE_VERSION "\n"
"\n"
"Usage:\n"
" Create quad image:\n"
" %1$s [options] -H <num> -o <outfile> <infile> "
"<infile> <infile> <infile>\n"
" Create single image:\n"
" %1$s [options] -H <num> -o <outfile> <infile>\n"
"\n"
"Options:\n"
"\n"
"-h (--help) Display this help message and exit\n"
"-H (--header-version) <num> Header version to be created\n"
" - Arria/Cyclone V = 0\n"
" - Arria 10 = 1\n"
" [default: 0]\n"
"-a (--alignment) <num> Address alignment in kilobytes, valid value\n"
" starts from 64, 128, 256 etc, override if the\n"
" NAND flash has a larger block size\n"
" [default: 64 for header v0, 256 for v1]\n"
"-o (--output) <outfile> Output file. Relative and absolute path\n"
" supported\n"
"-b (--barebox) Add barebox header\n"
"-O (--offset) <num> Program entry offset relative to start of\n"
" header (0x40). Used for header version 1 only.\n"
" [default: 20, min: 20]\n", bin);
}
int main(int argc, char **argv) {
static struct option opts[] = {
{"help", no_argument, NULL, 'h' },
{"header-version", required_argument, NULL, 'H' },
{"alignement", required_argument, NULL, 'a' },
{"output", required_argument, NULL, 'o' },
{"barebox", no_argument, NULL, 'b' },
{"offset", required_argument, NULL, 'O' },
{NULL, 0, NULL, 0 },
};
int opt;
int barebox = 0;
unsigned header_version = 0;
size_t alignment = 0;
int alignment_set = 0;
const char *outfile = NULL;
unsigned nfiles;
unsigned long entry_offset = 20;
int entry_offset_set = 0;
int outfd;
unsigned i;
while ((opt = getopt_long(argc, argv, "hH:a:dfo:bO:", opts, NULL)) != -1) {
switch (opt) {
case 'H':
header_version = optarg[0] - '0';
if (strlen(optarg) != 1 ||
(header_version != 0 && header_version != 1)) {
fprintf(stderr, "Invalid header version: %s\n\n", optarg);
usage(argv[0]);
return EXIT_FAILURE;
}
break;
case 'a':
{
char *endptr;
alignment = strtoul(optarg, &endptr, 0);
if (*endptr != '\0' || alignment == 0) {
fprintf(stderr, "Invalid alignment: %s\n\n", optarg);
usage(argv[0]);
return EXIT_FAILURE;
}
alignment *= 1024;
alignment_set = 1;
}
break;
case 'b':
barebox = 1;
break;
case 'o':
outfile = optarg;
break;
case 'O':
{
char *endptr;
entry_offset = strtoul(optarg, &endptr, 0);
if (*endptr != '\0' || entry_offset < 20) {
fprintf(stderr, "Invalid entry offset: %s\n\n", optarg);
usage(argv[0]);
return EXIT_FAILURE;
}
entry_offset_set = 1;
}
break;
case 'h':
default:
usage(argv[0]);
return EXIT_FAILURE;
}
}
if (outfile == NULL) {
fprintf(stderr, "Missing output file name\n\n");
usage(argv[0]);
return EXIT_FAILURE;
}
if (!alignment_set) {
if (header_version == 0) {
/* Default to 64K */
alignment = 64 * 1024;
} else {
/* V1 defaults to 256K */
alignment = 256 * 1024;
}
}
if (entry_offset_set && header_version == 0) {
fprintf(stderr,
"Error: entry offset is not available in header version 0\n\n");
usage(argv[0]);
return EXIT_FAILURE;
}
nfiles = argc - optind;
if (nfiles != 1 && nfiles != 4) {
fprintf(stderr,
"Wrong number of input files: expected 1 or 4, got %u\n\n",
nfiles);
usage(argv[0]);
return EXIT_FAILURE;
}
outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (outfd == -1) {
fprintf(stderr,
"Couldn't open '%s' for writing: %s\n",
outfile, strerror(errno));
return EXIT_FAILURE;
}
/* Barebox's version of this program doesn't pad the output up to
* alignment, however Altera's mkpimage does. */
if (ftruncate(outfd, alignment * nfiles) == -1) {
fprintf(stderr,
"Couldn't truncate '%s' to %lu: %s\n",
outfile, alignment * nfiles, strerror(errno));
goto unlink;
}
for (i = 0; i < nfiles; i++) {
if (lseek(outfd, i * alignment, SEEK_SET) == -1) {
fprintf(stderr,
"Couldn't seek '%s' to %lu: %s\n",
outfile, i * alignment, strerror(errno));
goto unlink;
}
if (add_file(outfd,
argv[optind + i],
header_version,
alignment,
barebox,
entry_offset) == -1) {
goto unlink;
}
}
close(outfd);
return EXIT_SUCCESS;
unlink:
/* Something went wrong, delete the output*/
close(outfd);
unlink(outfile);
return EXIT_FAILURE;
}
static int add_file(int outfd,
const char *path,
unsigned header_version,
size_t alignment,
int barebox,
unsigned long entry_offset) {
int fd = -1;
struct stat st;
int ret = -1;
size_t file_size;
size_t out_size;
uint8_t *buffer = NULL;
uint8_t *rbuffer = NULL;
size_t nread = 0;
fd = open(path, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "Couldn't open '%s' for reading: %s\n",
path, strerror(errno));
goto exit;
}
if (fstat(fd, &st) == -1) {
fprintf(stderr,
"Couldn't stat '%s': %s\n",
path, strerror(errno));
goto exit;
}
file_size = st.st_size;
/* Add room for 32bit CRC */
out_size = file_size + sizeof(uint32_t);
if (barebox) {
out_size += 512;
}
/* Barebox's version of this program pads the binary to a multiple
* of 4bytes, however altera's mkpimage pads to 32bytes. I'm not
* sure if it makes a difference in practice but I prefer to be as
* close as possible to the reference.
*/
out_size = (out_size + 31UL) & (~31UL);
if (out_size > alignment) {
fprintf(stderr,
"Error: File '%s' is too big to fit the alignment of %luKB\n",
path,
alignment / 1024);
goto exit;
}
buffer = malloc(out_size);
if (buffer == NULL) {
fprintf(stderr, "malloc(%lu) failed\n", out_size);
goto exit;
}
memset(buffer, 0, out_size);
if (barebox) {
memcpy(buffer, bb_header, sizeof(bb_header));
rbuffer = buffer + 512;
} else {
rbuffer = buffer;
}
while (nread < file_size) {
ssize_t n = read(fd, rbuffer + nread, file_size - nread);
if (n < 0) {
fprintf(stderr, "Error while reading '%s': %s\n",
path, strerror(errno));
goto exit;
}
if (n == 0) {
fprintf(stderr, "Error while reading '%s': %s\n",
path, "Short read");
goto exit;
}
nread += (size_t)n;
}
if (header_version == 0) {
add_socfpga_header_v0(buffer, out_size);
} else {
add_socfpga_header_v1(buffer, out_size, entry_offset);
}
if (write_all(outfd, buffer, out_size) == -1) {
goto exit;
}
ret = 0;
exit:
if (buffer != NULL) {
free(buffer);
}
if (fd >= 0) {
close(fd);
}
return ret;
}
static void add_socfpga_header_v0(uint8_t *buf, size_t size)
{
struct socfpga_header_v0 *header = (void*)(buf + 0x40);
uint32_t crc;
uint32_t crc_idx;
unsigned checksum;
size_t length = size >> 2;
size_t i;
header->validation_word[0] = VALIDATION_WORD & 0xff;
header->validation_word[1] = (VALIDATION_WORD >> 8) & 0xff;
header->validation_word[2] = (VALIDATION_WORD >> 16) & 0xff;
header->validation_word[3] = (VALIDATION_WORD >> 24) & 0xff;
header->version = 0;
header->flags = 0;
header->program_length[0] = length & 0xff;
header->program_length[1] = (length >> 8) & 0xff;
header->spare[0] = 0;
header->spare[1] = 0;
/* Header checksum */
checksum = 0;
for (i = 0; i < sizeof(*header) - 2; i++)
checksum += buf[i + 0x40];
header->checksum[0] = checksum & 0xff;
header->checksum[1] = (checksum >> 8) & 0xff;
crc = crc32(buf, size - sizeof(uint32_t));
crc_idx = size - sizeof(uint32_t);
buf[crc_idx++] = crc & 0xff;
buf[crc_idx++] = (crc >> 8) & 0xff;
buf[crc_idx++] = (crc >> 16) & 0xff;
buf[crc_idx++] = (crc >> 24) & 0xff;
}
static void add_socfpga_header_v1(uint8_t *buf,
size_t size,
unsigned long entry_offset) {
struct socfpga_header_v1 *header = (void*)(buf + 0x40);
uint32_t crc;
uint32_t crc_idx;
unsigned checksum;
int i;
header->validation_word[0] = VALIDATION_WORD & 0xff;
header->validation_word[1] = (VALIDATION_WORD >> 8) & 0xff;
header->validation_word[2] = (VALIDATION_WORD >> 16) & 0xff;
header->validation_word[3] = (VALIDATION_WORD >> 24) & 0xff;
header->version = 1;
header->flags = 0;
header->header_length[0] = sizeof(*header) & 0xff;
header->header_length[1] = (sizeof(*header) >> 8) & 0xff;
header->program_length[0] = size & 0xff;
header->program_length[1] = (size >> 8) & 0xff;
header->program_length[2] = (size >> 16) & 0xff;
header->program_length[3] = (size >> 24) & 0xff;
header->program_offset[0] = entry_offset & 0xff;
header->program_offset[1] = (entry_offset >> 8) & 0xff;
header->program_offset[2] = (entry_offset >> 16) & 0xff;
header->program_offset[3] = (entry_offset >> 24) & 0xff;
header->spare[0] = 0;
header->spare[1] = 0;
/* Header checksum */
checksum = 0;
for (i = 0; i < sizeof(*header) - 2; i++)
checksum += buf[i + 0x40];
header->checksum[0] = checksum & 0xff;
header->checksum[1] = (checksum >> 8) & 0xff;
crc = crc32(buf, size - sizeof(uint32_t));
crc_idx = size - sizeof(uint32_t);
buf[crc_idx++] = crc & 0xff;
buf[crc_idx++] = (crc >> 8) & 0xff;
buf[crc_idx++] = (crc >> 16) & 0xff;
buf[crc_idx++] = (crc >> 24) & 0xff;
}
static int write_all(int fd, uint8_t *buf, size_t size)
{
while (size) {
ssize_t written = write(fd, buf, size);
if (written < 0) {
fprintf(stderr, "Error while writing: %s\n", strerror(errno));
return -1;
}
if (written == 0) {
fprintf(stderr, "Error while writing: short write\n");
return -1;
}
size -= (size_t)written;
buf += written;
}
return 0;
}
static uint32_t crc_table[256] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};
uint32_t crc32(uint8_t *buf, size_t length)
{
uint32_t crc = 0xffffffff;
while (length--) {
crc = crc << 8 ^ crc_table[(crc >> 24 ^ *(buf++)) & 0xff];
}
return crc ^ 0xffffffff;
}
@expipiplus1
Copy link

expipiplus1 commented Jul 8, 2017

The SoCEDS tool aborts when the image is greater than 204796 bytes, this tool doesn't seem to do the same.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment