1 #include <stdio.h>
2 #include <unistd.h>
3 #include <getopt.h>
4 #include <stdlib.h>
5 #include <stdint.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <fcntl.h>
11 #include <endian.h>
12
13 #define VALIDATION_WORD 0x31305341
14
15 #define BRANCH_INST 0xea /* ARM opcode for "b" (unconditional branch) */
16
17 #define MAX_V0IMAGE_SIZE (60 * 1024 - 4)
18 /* Max size without authentication is 224 KB, due to memory used by
19 * the ROM boot code as a workspace out of the 256 KB of OCRAM */
20 #define MAX_V1IMAGE_SIZE (224 * 1024 - 4)
21
22 static int add_barebox_header;
23
24 struct socfpga_header {
25 uint8_t validation_word[4];
26 uint8_t version;
27 uint8_t flags;
28 union {
29 struct {
30 uint8_t program_length[2];
31 uint8_t spare[2];
32 uint8_t checksum[2];
33 uint8_t start_vector[4];
34 } v0;
35 struct {
36 uint8_t header_length[2];
37 uint8_t program_length[4];
38 uint8_t entry_offset[4];
39 uint8_t spare[2];
40 uint8_t checksum[2];
41 } v1;
42 };
43 };
44
45 static uint32_t bb_header[] = {
46 0xea00007e, /* b 0x200 */
47 0xeafffffe, /* 1: b 1b */
48 0xeafffffe, /* 1: b 1b */
49 0xeafffffe, /* 1: b 1b */
50 0xeafffffe, /* 1: b 1b */
51 0xeafffffe, /* 1: b 1b */
52 0xeafffffe, /* 1: b 1b */
53 0xeafffffe, /* 1: b 1b */
54 0x65726162, /* 'bare' */
55 0x00786f62, /* 'box\0' */
56 0x00000000, /* padding */
57 0x00000000, /* padding */
58 0x00000000, /* padding */
59 0x00000000, /* padding */
60 0x00000000, /* padding */
61 0x00000000, /* padding */
62 0x00000000, /* socfpga header */
63 0x00000000, /* socfpga header */
64 0x00000000, /* socfpga header */
65 0xea00006b, /* entry. b 0x200 (offset may be adjusted) */
66 };
67
read_full(int fd,void * buf,size_t size)68 static int read_full(int fd, void *buf, size_t size)
69 {
70 size_t insize = size;
71 int now;
72 int total = 0;
73
74 while (size) {
75 now = read(fd, buf, size);
76 if (now == 0)
77 return total;
78 if (now < 0)
79 return now;
80 total += now;
81 size -= now;
82 buf += now;
83 }
84
85 return insize;
86 }
87
write_full(int fd,void * buf,size_t size)88 static int write_full(int fd, void *buf, size_t size)
89 {
90 size_t insize = size;
91 int now;
92
93 while (size) {
94 now = write(fd, buf, size);
95 if (now <= 0)
96 return now;
97 size -= now;
98 buf += now;
99 }
100
101 return insize;
102 }
103
104 static const uint32_t crc_table[256] = {
105 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
106 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
107 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
108 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
109 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
110 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
111 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
112 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
113 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
114 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
115 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
116 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
117 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
118 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
119 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
120 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
121 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
122 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
123 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
124 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
125 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
126 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
127 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
128 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
129 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
130 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
131 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
132 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
133 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
134 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
135 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
136 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
137 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
138 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
139 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
140 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
141 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
142 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
143 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
144 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
145 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
146 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
147 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
148 };
149
crc32(uint32_t crc,void * _buf,int length)150 uint32_t crc32(uint32_t crc, void *_buf, int length)
151 {
152 uint8_t *buf = _buf;
153
154 while (length--)
155 crc = crc << 8 ^ crc_table[(crc >> 24 ^ *(buf++)) & 0xff];
156
157 return crc;
158 }
159
160 /* Create an ARM relative branch instuction
161 * branch is where the instruction will be placed and dest points to where
162 * it should branch too. */
branch(uint8_t * branch,uint8_t * dest)163 static void branch(uint8_t *branch, uint8_t *dest)
164 {
165 int offset = dest - branch - 8; /* PC is offset +8 bytes on ARM */
166
167 branch[0] = (offset >> 2) & 0xff; /* instruction uses offset/4 */
168 branch[1] = (offset >> 10) & 0xff;
169 branch[2] = (offset >> 18) & 0xff;
170 branch[3] = BRANCH_INST;
171 }
172
173 /* start_addr is where the socfpga header's start instruction should branch to.
174 * It should be relative to the start of buf */
add_socfpga_header(void * buf,size_t size,unsigned start_addr,unsigned version)175 static int add_socfpga_header(void *buf, size_t size, unsigned start_addr, unsigned version)
176 {
177 struct socfpga_header *header = buf + 0x40;
178 void *entry;
179 uint8_t *bufp, *sumendp;
180 uint32_t *crc;
181 unsigned checksum;
182
183 if (size & 0x3) {
184 fprintf(stderr, "%s: size must be multiple of 4\n", __func__);
185 return -EINVAL;
186 }
187
188 /* Absolute address of entry point in buf */
189 entry = buf + start_addr;
190 if (version == 0) {
191 sumendp = &header->v0.checksum[0];
192 } else {
193 sumendp = &header->v1.checksum[0];
194
195 /* The ROM loader can't handle a negative offset */
196 if (entry < (void*)header) {
197 /* add a trampoline branch inst after end of the header */
198 uint8_t *trampoline = (void*)(header + 1);
199 branch(trampoline, entry);
200
201 /* and then make the trampoline the entry point */
202 entry = trampoline;
203 }
204 /* Calculate start address as offset relative to start of header */
205 start_addr = entry - (void*)header;
206 }
207
208 header->validation_word[0] = VALIDATION_WORD & 0xff;
209 header->validation_word[1] = (VALIDATION_WORD >> 8) & 0xff;
210 header->validation_word[2] = (VALIDATION_WORD >> 16) & 0xff;
211 header->validation_word[3] = (VALIDATION_WORD >> 24) & 0xff;
212 header->version = version;
213 header->flags = 0;
214
215 if (version == 0) {
216 header->v0.program_length[0] = (size >> 2) & 0xff; /* length in words */
217 header->v0.program_length[1] = (size >> 10) & 0xff;
218 header->v0.spare[0] = 0;
219 header->v0.spare[1] = 0;
220 branch(header->v0.start_vector, entry);
221 } else {
222 header->v1.header_length[0] = (sizeof(*header) >> 0) & 0xff;
223 header->v1.header_length[1] = (sizeof(*header) >> 8) & 0xff;
224 header->v1.program_length[0] = (size >> 0) & 0xff;
225 header->v1.program_length[1] = (size >> 8) & 0xff;
226 header->v1.program_length[2] = (size >> 16) & 0xff;
227 header->v1.program_length[3] = (size >> 24) & 0xff;
228 header->v1.entry_offset[0] = (start_addr >> 0) & 0xff;
229 header->v1.entry_offset[1] = (start_addr >> 8) & 0xff;
230 header->v1.entry_offset[2] = (start_addr >> 16) & 0xff;
231 header->v1.entry_offset[3] = (start_addr >> 24) & 0xff;
232 header->v1.spare[0] = 0;
233 header->v1.spare[1] = 0;
234 }
235
236 /* Sum from beginning of header to start of checksum field */
237 checksum = 0;
238 for (bufp = (uint8_t*)header; bufp < sumendp; bufp++)
239 checksum += *bufp;
240
241 if (version == 0) {
242 header->v0.checksum[0] = checksum & 0xff;;
243 header->v0.checksum[1] = (checksum >> 8) & 0xff;;
244 } else {
245 header->v1.checksum[0] = checksum & 0xff;;
246 header->v1.checksum[1] = (checksum >> 8) & 0xff;;
247 }
248
249 crc = buf + size - sizeof(uint32_t);
250
251 *crc = crc32(0xffffffff, buf, size - sizeof(uint32_t));
252 *crc ^= 0xffffffff;
253
254 return 0;
255 }
256
usage(const char * prgname)257 static void usage(const char *prgname)
258 {
259 fprintf(stderr, "usage: %s [-hb] [-v version] <infile> -o <outfile>\n", prgname);
260 }
261
main(int argc,char * argv[])262 int main(int argc, char *argv[])
263 {
264 int opt, ret;
265 const char *outfile = NULL, *infile;
266 struct stat s;
267 void *buf;
268 int fd;
269 int max_image_size, min_image_size = 80;
270 int addsize = 0, pad;
271 unsigned int version = 0;
272
273 while ((opt = getopt(argc, argv, "o:hbv:")) != -1) {
274 switch (opt) {
275 case 'v':
276 version = atoi(optarg);
277 if (version > 1) {
278 printf("Versions supported: 0 or 1\n");
279 usage(argv[0]);
280 exit(1);
281 }
282 break;
283 case 'b':
284 add_barebox_header = 1;
285 min_image_size = 0;
286 addsize = 512;
287 break;
288 case 'h':
289 usage(argv[0]);
290 exit(0);
291 case 'o':
292 outfile = optarg;
293 break;
294 default:
295 usage(argv[0]);
296 exit(1);
297 }
298 }
299 if (version == 0) {
300 max_image_size = MAX_V0IMAGE_SIZE;
301 } else {
302 max_image_size = MAX_V1IMAGE_SIZE;
303 }
304 max_image_size -= addsize;
305
306 if (optind == argc || !outfile) {
307 usage(argv[0]);
308 exit(1);
309 }
310 infile = argv[optind];
311
312 ret = stat(infile, &s);
313 if (ret) {
314 perror("stat");
315 exit(1);
316 }
317
318 if (s.st_size < min_image_size) {
319 fprintf(stderr, "input image too small. Minimum is %d bytes\n",
320 min_image_size);
321 exit(1);
322 }
323
324 if (s.st_size > max_image_size) {
325 fprintf(stderr, "input image too big. Maximum is %d bytes, got %ld bytes\n",
326 max_image_size, s.st_size);
327 exit(1);
328 }
329
330 fd = open(infile, O_RDONLY);
331 if (fd == -1) {
332 perror("open infile");
333 exit(1);
334 }
335
336 pad = s.st_size & 0x3;
337 if (pad)
338 pad = 4 - pad;
339
340 buf = calloc(s.st_size + 4 + addsize + pad, 1);
341 if (!buf) {
342 perror("malloc");
343 exit(1);
344 }
345
346 ret = read_full(fd, buf + addsize, s.st_size);
347 if (ret < 0) {
348 perror("read infile");
349 exit(1);
350 }
351
352 close(fd);
353
354 if (add_barebox_header) {
355 memcpy(buf, bb_header, sizeof(bb_header));
356 }
357
358 ret = add_socfpga_header(buf, s.st_size + 4 + addsize + pad, addsize,
359 version);
360 if (ret)
361 exit(1);
362
363 fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
364 if (fd < 0) {
365 perror("open outfile");
366 exit(1);
367 }
368
369 ret = write_full(fd, buf, s.st_size + 4 + addsize + pad);
370 if (ret < 0) {
371 perror("write outfile");
372 exit(1);
373 }
374
375 exit(0);
376 }
377