blob: 58765b1a6f327a6c8fae8e955f33867c0b88f2a6 [file] [log] [blame]
Alexandru Gagniuc910ce012014-01-10 23:06:03 -06001/*
2 * A simple tool to generate bootable image for sunxi platform.
3 *
4 * Copyright (C) 2007-2011 Allwinner Technology Co., Ltd.
5 * Tom Cubie <tangliang@allwinnertech.com>
6 * Copyright (C) 2014 Alexandru Gagniuc <mr.nuke.me@gmail.com>
7 * Subject to the GNU GPL v2, or (at your option) any later version.
8 */
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <stdint.h>
13#include <string.h>
Patrick Georgi32eeff42014-08-11 09:27:18 +020014#include <sys/types.h>
15#include <sys/stat.h>
16#include <unistd.h>
Alexandru Gagniuc910ce012014-01-10 23:06:03 -060017#include <errno.h>
18
19/* boot head definition from sun4i boot code */
20struct boot_file_head {
21 uint32_t jump_instruction; /* one intruction jumping to real code */
22 uint8_t magic[8]; /* ="eGON.BT0" or "eGON.BT1", not C-style str */
23 uint32_t check_sum; /* generated by PC */
24 uint32_t length; /* generated by PC */
25 /* We use a simplified header, only filling in what is needed by the
26 * boot ROM. To be compatible with Allwinner tools the larger header
27 * below should be used, followed by a custom header if desired. */
28 uint8_t pad[12]; /* align to 32 bytes */
29};
30
31static const char *BOOT0_MAGIC = "eGON.BT0";
32static const uint32_t STAMP_VALUE = 0x5F0A6C39;
33static const int HEADER_SIZE = 32;
34/* Checksum at most 24 KiB */
35#define SRAM_LOAD_MAX_SIZE ((24 << 10) - sizeof(struct boot_file_head))
36static const int BLOCK_SIZE = 512;
37
38inline static uint32_t le32_to_h(const void *src)
39{
40 const uint8_t *b = src;
41 return ((b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0] << 0));
42}
43
44inline static void h_to_le32(uint32_t val32, void *dest)
45{
46 uint8_t *b = dest;
47 b[0] = (val32 >> 0) & 0xff;
48 b[1] = (val32 >> 8) & 0xff;
49 b[2] = (val32 >> 16) & 0xff;
50 b[3] = (val32 >> 24) & 0xff;
51};
52
53static void serialize_header(void *dest, const struct boot_file_head *hdr)
54{
55 /* Unused fields are zero */
56 memset(dest, 0, HEADER_SIZE);
57
58 h_to_le32(hdr->jump_instruction, dest + 0);
59 memcpy(dest + 4, BOOT0_MAGIC, 8);
60 h_to_le32(hdr->check_sum, dest + 12);
61 h_to_le32(hdr->length, dest + 16);
62}
63
64/* Check sum function from sun4i boot code */
65static int fill_check_sum(struct boot_file_head *hdr, const void *boot_code)
66{
67 size_t i;
68 uint8_t raw_hdr[HEADER_SIZE];
69 uint32_t chksum;
70
71 if ((hdr->length & 0x3) != 0) {
72 fprintf(stderr, "BUG! Load size is not 4-byte aligned\n");
73 return EXIT_FAILURE;
74 }
75
76 /* Fill in checksum seed */
77 hdr->check_sum = STAMP_VALUE;
78
79 chksum = 0;
80 /* Checksum the header */
81 serialize_header(raw_hdr, hdr);
82 for (i = 0; i < HEADER_SIZE; i += 4)
83 chksum += le32_to_h(raw_hdr + i);
84
85 /* Checksum the boot code */
86 for (i = 0; i < hdr->length - HEADER_SIZE; i += 4)
87 chksum += le32_to_h(boot_code + i);
88
89 /* write back check sum */
90 hdr->check_sum = chksum;
91
92 return EXIT_SUCCESS;
93}
94
95static uint32_t align(uint32_t size, uint32_t alignment)
96{
97 return ((size + alignment - 1) / alignment) * alignment;
98}
99
100static void fill_header(struct boot_file_head *hdr, size_t load_size)
101{
102 /* B instruction */
103 hdr->jump_instruction = 0xEA000000;
104 /* Jump to the first instr after the header */
105 hdr->jump_instruction |= (sizeof(*hdr) / sizeof(uint32_t) - 2);
106 /* No '0' termination in magic string */
107 memcpy(&hdr->magic, BOOT0_MAGIC, 8);
108
109 hdr->length = align(load_size + sizeof(hdr), BLOCK_SIZE);
110}
111
112static long int fsize(FILE *file)
113{
Patrick Georgi32eeff42014-08-11 09:27:18 +0200114 struct stat s;
115 int fd = fileno(file);
116 if (fd == -1) return -1;
117 if (fstat(fd, &s) == -1) return -1;
118 return s.st_size;
Alexandru Gagniuc910ce012014-01-10 23:06:03 -0600119}
120
121int main(int argc, char *argv[])
122{
123 FILE *fd_in, *fd_out;
124 struct boot_file_head hdr;
125 long int file_size, load_size;
126 void *file_data;
127 uint8_t raw_hdr[HEADER_SIZE];
128 int count;
129
130 /*
131 * TODO: We could take an additional argument to see how much of the
132 * file to checksum. This way, the build system can tell us how large
133 * the bootblock is, so we can tell the BROM to load only the bootblock.
134 */
135 if (argc < 2) {
136 printf("\tThis program makes an input bin file to sun4i "
137 "bootable image.\n"
138 "\tUsage: %s input_file out_putfile\n", argv[0]);
139 return EXIT_FAILURE;
140 }
141
Scott Duplichane1a4dc82014-12-12 21:34:04 -0600142 fd_in = fopen(argv[1], "rb");
Alexandru Gagniuc910ce012014-01-10 23:06:03 -0600143 if (!fd_in) {
144 fprintf(stderr, "Cannot open input %s", argv[1]);
145 return EXIT_FAILURE;
146 }
147
148 /* Get input file size */
149 file_size = fsize(fd_in);
Patrick Georgi32eeff42014-08-11 09:27:18 +0200150 if (file_size == -1) {
151 fprintf(stderr, "can't determine file size\n");
152 return EXIT_FAILURE;
153 }
Alexandru Gagniuc910ce012014-01-10 23:06:03 -0600154 if ((file_data = malloc(file_size)) == NULL) {
155 fprintf(stderr, "Cannot allocate memory\n");
156 return EXIT_FAILURE;
157 }
158
159 printf("File size: 0x%x\n", file_size);
160 if (fread(file_data, file_size, 1, fd_in) != 1) {
161 fprintf(stderr, "Cannot read %s: %s\n", argv[1],
162 strerror(errno));
163 return EXIT_FAILURE;
164 }
165
166 load_size = align(file_size, sizeof(uint32_t));
167
168 if (load_size > SRAM_LOAD_MAX_SIZE)
169 load_size = SRAM_LOAD_MAX_SIZE;
170
171 printf("Load size: 0x%x\n", load_size);
172
173 fd_out = fopen(argv[2], "w");
174 if (!fd_out) {
175 fprintf(stderr, "Cannot open output %s\n", argv[2]);
176 return EXIT_FAILURE;
177 }
178
179 /* Fill the header */
180 fill_header(&hdr, load_size);
181 fill_check_sum(&hdr, file_data);
182
183 /* Now write the header */
184 serialize_header(raw_hdr, &hdr);
185 if (fwrite(raw_hdr, HEADER_SIZE, 1, fd_out) != 1) {
186 fprintf(stderr, "Cannot write header to %s: %s\n", argv[1],
187 strerror(errno));
188 return EXIT_FAILURE;
189 }
190
191 /* And finally, the boot code */
192 if (fwrite(file_data, file_size, 1, fd_out) != 1) {
193 fprintf(stderr, "Cannot write to %s: %s\n", argv[1],
194 strerror(errno));
195 return EXIT_FAILURE;
196 }
197
198 fclose(fd_in);
199 fclose(fd_out);
200
201 return EXIT_SUCCESS;
202}