diff --git a/src/cpu/allwinner/a10/Makefile.inc b/src/cpu/allwinner/a10/Makefile.inc index 03bc612457..1b720bf779 100644 --- a/src/cpu/allwinner/a10/Makefile.inc +++ b/src/cpu/allwinner/a10/Makefile.inc @@ -30,16 +30,25 @@ get_bootblock_size= \ sed 's/[^0-9 ]//g')) \ $(shell echo $$(($(word 2, $(strip $(bb_s)))))) +# This tool is used to prepend a header to coreboot.rom to trick the SoC into +# loading out bootblock +# +MKSUNXIBOOT:=$(objutil)/mksunxiboot +$(MKSUNXIBOOT): $(top)/util/arm_boot_tools/mksunxiboot/mksunxiboot.c + @printf " HOSTCC $(subst $(obj)/,,$(@))\n" + $(HOSTCC) $(HOSTCFLAGS) -o $@ $< + # The boot ROM in the SoC will start loading code if a special BOOT0 header is # found (at an offset of 8KiB in either NAND or SD), and the checksum is -# correct. This header is normally added by the 'mxsunxiboot' tool. The boot ROM -# will load at most 24KiB of data to SRAM, so limit the file size accordingly. -# The BOOT0 header takes 32 bytes, so limit our file to 24KiB - 32 bytes. -# FIXME: Figure out how to safely integrate in coreboot.rom. -# FIXME: The file passed to mksunxiboot should only include the bootblock due -# to size limitations. -$(obj)/BOOT0: $(obj)/coreboot.rom +# correct. This header is added by the 'mxsunxiboot' tool, which is provided +# under util/arm_boot_tools/mksunxiboot. The boot ROM will load at most 24KiB of +# data to SRAM. The BOOT0 header takes 32 bytes, so bootblock is limited to +# 24KiB - 32 bytes. +# TODO: make mksunxiboot take the bootblock size as a parameter +# TODO: print an error if bootblock is too large (maybe place ROMSTAGE at the +# exact offset needed to collide with the bootblock) +# FIXME: A10 loads 24KiB. According to Oliver other chips load a little more +# +$(obj)/BOOT0: $(obj)/coreboot.rom $(MKSUNXIBOOT) @printf " BOOT0 $(subst $(obj)/,,$(^))\n" - touch $@ - dd if=$^ of=$^.tmp bs=24544 count=1 - -mksunxiboot $^.tmp $@ + $(MKSUNXIBOOT) $(word 1, $^) $@ diff --git a/util/arm_boot_tools/mksunxiboot/mksunxiboot.c b/util/arm_boot_tools/mksunxiboot/mksunxiboot.c new file mode 100644 index 0000000000..af8450c338 --- /dev/null +++ b/util/arm_boot_tools/mksunxiboot/mksunxiboot.c @@ -0,0 +1,196 @@ +/* + * A simple tool to generate bootable image for sunxi platform. + * + * Copyright (C) 2007-2011 Allwinner Technology Co., Ltd. + * Tom Cubie + * Copyright (C) 2014 Alexandru Gagniuc + * Subject to the GNU GPL v2, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +/* 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) +{ + long int size; + + fseek(file, 0L, SEEK_END); + size = ftell(file); + fseek(file, 0L, SEEK_SET); + return 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], "r"); + 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_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; +}