/* * 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; }