xref: /OK3568_Linux_fs/buildroot/package/mkpimage/mkpimage.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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