blob: 58765b1a6f327a6c8fae8e955f33867c0b88f2a6 [file] [log] [blame]
/*
* A simple tool to generate bootable image for sunxi platform.
*
* Copyright (C) 2007-2011 Allwinner Technology Co., Ltd.
* Tom Cubie <tangliang@allwinnertech.com>
* Copyright (C) 2014 Alexandru Gagniuc <mr.nuke.me@gmail.com>
* Subject to the GNU GPL v2, or (at your option) any later version.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
/* boot head definition from sun4i boot code */
struct boot_file_head {
uint32_t jump_instruction; /* one intruction jumping to real code */
uint8_t magic[8]; /* ="eGON.BT0" or "eGON.BT1", not C-style str */
uint32_t check_sum; /* generated by PC */
uint32_t length; /* generated by PC */
/* We use a simplified header, only filling in what is needed by the
* boot ROM. To be compatible with Allwinner tools the larger header
* below should be used, followed by a custom header if desired. */
uint8_t pad[12]; /* align to 32 bytes */
};
static const char *BOOT0_MAGIC = "eGON.BT0";
static const uint32_t STAMP_VALUE = 0x5F0A6C39;
static const int HEADER_SIZE = 32;
/* Checksum at most 24 KiB */
#define SRAM_LOAD_MAX_SIZE ((24 << 10) - sizeof(struct boot_file_head))
static const int BLOCK_SIZE = 512;
inline static uint32_t le32_to_h(const void *src)
{
const uint8_t *b = src;
return ((b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0] << 0));
}
inline static void h_to_le32(uint32_t val32, void *dest)
{
uint8_t *b = dest;
b[0] = (val32 >> 0) & 0xff;
b[1] = (val32 >> 8) & 0xff;
b[2] = (val32 >> 16) & 0xff;
b[3] = (val32 >> 24) & 0xff;
};
static void serialize_header(void *dest, const struct boot_file_head *hdr)
{
/* Unused fields are zero */
memset(dest, 0, HEADER_SIZE);
h_to_le32(hdr->jump_instruction, dest + 0);
memcpy(dest + 4, BOOT0_MAGIC, 8);
h_to_le32(hdr->check_sum, dest + 12);
h_to_le32(hdr->length, dest + 16);
}
/* Check sum function from sun4i boot code */
static int fill_check_sum(struct boot_file_head *hdr, const void *boot_code)
{
size_t i;
uint8_t raw_hdr[HEADER_SIZE];
uint32_t chksum;
if ((hdr->length & 0x3) != 0) {
fprintf(stderr, "BUG! Load size is not 4-byte aligned\n");
return EXIT_FAILURE;
}
/* Fill in checksum seed */
hdr->check_sum = STAMP_VALUE;
chksum = 0;
/* Checksum the header */
serialize_header(raw_hdr, hdr);
for (i = 0; i < HEADER_SIZE; i += 4)
chksum += le32_to_h(raw_hdr + i);
/* Checksum the boot code */
for (i = 0; i < hdr->length - HEADER_SIZE; i += 4)
chksum += le32_to_h(boot_code + i);
/* write back check sum */
hdr->check_sum = chksum;
return EXIT_SUCCESS;
}
static uint32_t align(uint32_t size, uint32_t alignment)
{
return ((size + alignment - 1) / alignment) * alignment;
}
static void fill_header(struct boot_file_head *hdr, size_t load_size)
{
/* B instruction */
hdr->jump_instruction = 0xEA000000;
/* Jump to the first instr after the header */
hdr->jump_instruction |= (sizeof(*hdr) / sizeof(uint32_t) - 2);
/* No '0' termination in magic string */
memcpy(&hdr->magic, BOOT0_MAGIC, 8);
hdr->length = align(load_size + sizeof(hdr), BLOCK_SIZE);
}
static long int fsize(FILE *file)
{
struct stat s;
int fd = fileno(file);
if (fd == -1) return -1;
if (fstat(fd, &s) == -1) return -1;
return s.st_size;
}
int main(int argc, char *argv[])
{
FILE *fd_in, *fd_out;
struct boot_file_head hdr;
long int file_size, load_size;
void *file_data;
uint8_t raw_hdr[HEADER_SIZE];
int count;
/*
* TODO: We could take an additional argument to see how much of the
* file to checksum. This way, the build system can tell us how large
* the bootblock is, so we can tell the BROM to load only the bootblock.
*/
if (argc < 2) {
printf("\tThis program makes an input bin file to sun4i "
"bootable image.\n"
"\tUsage: %s input_file out_putfile\n", argv[0]);
return EXIT_FAILURE;
}
fd_in = fopen(argv[1], "rb");
if (!fd_in) {
fprintf(stderr, "Cannot open input %s", argv[1]);
return EXIT_FAILURE;
}
/* Get input file size */
file_size = fsize(fd_in);
if (file_size == -1) {
fprintf(stderr, "can't determine file size\n");
return EXIT_FAILURE;
}
if ((file_data = malloc(file_size)) == NULL) {
fprintf(stderr, "Cannot allocate memory\n");
return EXIT_FAILURE;
}
printf("File size: 0x%x\n", file_size);
if (fread(file_data, file_size, 1, fd_in) != 1) {
fprintf(stderr, "Cannot read %s: %s\n", argv[1],
strerror(errno));
return EXIT_FAILURE;
}
load_size = align(file_size, sizeof(uint32_t));
if (load_size > SRAM_LOAD_MAX_SIZE)
load_size = SRAM_LOAD_MAX_SIZE;
printf("Load size: 0x%x\n", load_size);
fd_out = fopen(argv[2], "w");
if (!fd_out) {
fprintf(stderr, "Cannot open output %s\n", argv[2]);
return EXIT_FAILURE;
}
/* Fill the header */
fill_header(&hdr, load_size);
fill_check_sum(&hdr, file_data);
/* Now write the header */
serialize_header(raw_hdr, &hdr);
if (fwrite(raw_hdr, HEADER_SIZE, 1, fd_out) != 1) {
fprintf(stderr, "Cannot write header to %s: %s\n", argv[1],
strerror(errno));
return EXIT_FAILURE;
}
/* And finally, the boot code */
if (fwrite(file_data, file_size, 1, fd_out) != 1) {
fprintf(stderr, "Cannot write to %s: %s\n", argv[1],
strerror(errno));
return EXIT_FAILURE;
}
fclose(fd_in);
fclose(fd_out);
return EXIT_SUCCESS;
}