summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cpu/allwinner/a10/Makefile.inc29
-rw-r--r--util/arm_boot_tools/mksunxiboot/mksunxiboot.c196
2 files changed, 215 insertions, 10 deletions
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 <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 <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)
+{
+ 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;
+}