blob: 766259114453237a7294d09fcf43ca8ca90c5f19 [file] [log] [blame]
Ketut Putu Kumajaya63846372014-06-23 20:05:33 +07001/* tools/mkbootimg/mkbootimg.c
2**
3** Copyright 2007, The Android Open Source Project
Ketut Putu Kumajaya72b1c492014-06-24 16:18:20 +07004** Copyright 2013, Sony Mobile Communications
Ketut Putu Kumajaya63846372014-06-23 20:05:33 +07005**
6** Licensed under the Apache License, Version 2.0 (the "License");
7** you may not use this file except in compliance with the License.
8** You may obtain a copy of the License at
9**
10** http://www.apache.org/licenses/LICENSE-2.0
11**
12** Unless required by applicable law or agreed to in writing, software
13** distributed under the License is distributed on an "AS IS" BASIS,
14** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15** See the License for the specific language governing permissions and
16** limitations under the License.
Ketut Putu Kumajaya72b1c492014-06-24 16:18:20 +070017**
18** June 2014, Ketut Putu Kumajaya
19** Modified for Samsung Exynos DTBH, based on mkqcdtbootimg from Sony
Ketut Putu Kumajaya63846372014-06-23 20:05:33 +070020*/
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26#include <fcntl.h>
27#include <errno.h>
28
Ketut Putu Kumajaya72b1c492014-06-24 16:18:20 +070029#include <sys/types.h>
30#include <arpa/inet.h>
31#include <assert.h>
32#include <dirent.h>
33#include <err.h>
34#include <stdint.h>
35
36#include "libfdt.h"
Ketut Putu Kumajaya63846372014-06-23 20:05:33 +070037#include "mincrypt/sha.h"
38#include "bootimg.h"
39
Ketut Putu Kumajaya72b1c492014-06-24 16:18:20 +070040#define DTBH_MAGIC "DTBH"
41#define DTBH_VERSION 2
42#define DTBH_PLATFORM "k3g"
43#define DTBH_SUBTYPE "k3g_eur_open"
44/* Hardcoded entry */
45#define DTBH_PLATFORM_CODE 0x1e92
46#define DTBH_SUBTYPE_CODE 0x7d64f612
47
48struct dt_blob;
49
50/* DTBH_MAGIC + DTBH_VERSION + DTB counts */
51#define DT_HEADER_PHYS_SIZE 12
52
53/* Samsung K 3G EUR revision 10's dts:
54 * model = "Samsung K 3G EUR revision 10 board based on EXYNOS5422";
55 * model_info-chip = <5422>;
56 * model_info-platform = "k3g";
57 * model_info-subtype = "k3g_eur_open";
58 * model_info-hw_rev = <10>;
59 * model_info-hw_rev_end = <255>;
60 * compatible = "samsung,K 3G EUR,r04", "samsung,exynos5422";
61 */
62
63/*
64 * keep the eight uint32_t entries first in this struct so we can memcpy them to the file
65 */
66#define DT_ENTRY_PHYS_SIZE (sizeof(uint32_t) * 8)
67struct dt_entry {
68 uint32_t chip;
69 uint32_t platform;
70 uint32_t subtype;
71 uint32_t hw_rev;
72 uint32_t hw_rev_end;
73 uint32_t offset;
74 uint32_t size; /* including padding */
75 uint32_t space;
76
77 struct dt_blob *blob;
78};
79
80/*
81 * Comparator for sorting dt_entries
82 */
83static int dt_entry_cmp(const void *ap, const void *bp)
84{
85 struct dt_entry *a = (struct dt_entry*)ap;
86 struct dt_entry *b = (struct dt_entry*)bp;
87
88 if (a->chip != b->chip)
89 return a->chip - b->chip;
90 return a->hw_rev - b->hw_rev;
91}
92
93/*
94 * In memory representation of a dtb blob
95 */
96struct dt_blob {
97 uint32_t size;
98 uint32_t offset;
99
100 void *payload;
101 struct dt_blob *next;
102};
103
Ketut Putu Kumajaya63846372014-06-23 20:05:33 +0700104static void *load_file(const char *fn, unsigned *_sz)
105{
106 char *data;
107 int sz;
108 int fd;
109
110 data = 0;
111 fd = open(fn, O_RDONLY);
112 if(fd < 0) return 0;
113
114 sz = lseek(fd, 0, SEEK_END);
115 if(sz < 0) goto oops;
116
117 if(lseek(fd, 0, SEEK_SET) != 0) goto oops;
118
119 data = (char*) malloc(sz);
120 if(data == 0) goto oops;
121
122 if(read(fd, data, sz) != sz) goto oops;
123 close(fd);
124
125 if(_sz) *_sz = sz;
126 return data;
127
128oops:
129 close(fd);
130 if(data != 0) free(data);
131 return 0;
132}
133
Ketut Putu Kumajaya72b1c492014-06-24 16:18:20 +0700134static void *load_dtbh_block(const char *dtb_path, unsigned pagesize, unsigned *_sz)
135{
136 const unsigned pagemask = pagesize - 1;
137 struct dt_entry *new_entries;
138 struct dt_entry *entries = NULL;
139 struct dt_entry *entry;
140 struct dt_blob *blob;
141 struct dt_blob *blob_list = NULL;
142 struct dt_blob *last_blob = NULL;
143 struct dirent *de;
144 unsigned new_count;
145 unsigned entry_count = 0;
146 unsigned offset;
147 unsigned dtb_sz;
148 unsigned hdr_sz = DT_HEADER_PHYS_SIZE;
149 uint32_t version = DTBH_VERSION;
150 unsigned blob_sz = 0;
151 char fname[PATH_MAX];
152 const unsigned *prop_chip;
153 const unsigned *prop_platform;
154 const unsigned *prop_subtype;
155 const unsigned *prop_hw_rev;
156 const unsigned *prop_hw_rev_end;
157 int namlen;
158 int len;
159 void *dtb;
160 char *dtbh;
161 DIR *dir;
162 unsigned c;
163
164 dir = opendir(dtb_path);
165
166 if (dir == NULL)
167 err(1, "failed to open '%s'", dtb_path);
168
169 while ((de = readdir(dir)) != NULL) {
170 namlen = strlen(de->d_name);
171 if (namlen < 4 || strcmp(&de->d_name[namlen - 4], ".dtb"))
172 continue;
173
174 snprintf(fname, sizeof(fname), "%s/%s", dtb_path, de->d_name);
175
176 dtb = load_file(fname, &dtb_sz);
177 if (dtb == NULL)
178 err(1, "failed to read dtb '%s'", fname);
179
180 if (fdt_check_header(dtb) != 0) {
181 warnx("'%s' is not a valid dtb, skipping", fname);
182 free(dtb);
183 continue;
184 }
185
186 offset = fdt_path_offset(dtb, "/");
187 prop_chip = fdt_getprop(dtb, offset, "model_info-chip", &len);
188 if (len % (sizeof(uint32_t)) != 0) {
189 warnx("model_info-chip of %s is of invalid size, skipping", fname);
190 free(dtb);
191 continue;
192 }
193
194 prop_platform = fdt_getprop(dtb, offset, "model_info-platform", &len);
195 if (strcmp((char *)&prop_platform[0], DTBH_PLATFORM)) {
196 warnx("model_info-platform of %s is invalid, skipping", fname);
197 free(dtb);
198 continue;
199 }
200
201 prop_subtype = fdt_getprop(dtb, offset, "model_info-subtype", &len);
202 if (strcmp((char *)&prop_subtype[0], DTBH_SUBTYPE)) {
203 warnx("model_info-subtype of %s is invalid, skipping", fname);
204 free(dtb);
205 continue;
206 }
207
208 prop_hw_rev = fdt_getprop(dtb, offset, "model_info-hw_rev", &len);
209 if (len % (sizeof(uint32_t)) != 0) {
210 warnx("model_info-hw_rev of %s is of invalid size, skipping", fname);
211 free(dtb);
212 continue;
213 }
214
215 prop_hw_rev_end = fdt_getprop(dtb, offset, "model_info-hw_rev_end", &len);
216 if (len % (sizeof(uint32_t)) != 0) {
217 warnx("model_info-hw_rev_end of %s is of invalid size, skipping", fname);
218 free(dtb);
219 continue;
220 }
221
222 blob = calloc(1, sizeof(struct dt_blob));
223 if (blob == NULL)
224 err(1, "failed to allocate memory");
225
226 blob->payload = dtb;
227 blob->size = dtb_sz;
228 if (blob_list == NULL) {
229 blob_list = blob;
230 last_blob = blob;
231 } else {
232 last_blob->next = blob;
233 last_blob = blob;
234 }
235
236 blob_sz += (blob->size + pagemask) & ~pagemask;
237 new_count = entry_count + 1;
238 new_entries = realloc(entries, new_count * sizeof(struct dt_entry));
239 if (new_entries == NULL)
240 err(1, "failed to allocate memory");
241
242 entries = new_entries;
243 entry = &entries[entry_count];
244 memset(entry, 0, sizeof(*entry));
245 entry->chip = ntohl(prop_chip[0]);
246 entry->platform = DTBH_PLATFORM_CODE;
247 entry->subtype = DTBH_SUBTYPE_CODE;
248 entry->hw_rev = ntohl(prop_hw_rev[0]);
249 entry->hw_rev_end = ntohl(prop_hw_rev_end[0]);
250 entry->space = 0x20; /* space delimiter */
251 entry->blob = blob;
252
253 entry_count++;
254
255 hdr_sz += entry_count * DT_ENTRY_PHYS_SIZE;
256 }
257
258 closedir(dir);
259
260 if (entry_count == 0) {
261 warnx("unable to locate any dtbs in the given path");
262 return NULL;
263 }
264
265 hdr_sz += sizeof(uint32_t); /* eot marker */
266 hdr_sz = (hdr_sz + pagemask) & ~pagemask;
267
268 qsort(entries, entry_count, sizeof(struct dt_entry), dt_entry_cmp);
269
270 /* The size of the dt header is now known, calculate the blob offsets... */
271 offset = hdr_sz;
272 for (blob = blob_list; blob; blob = blob->next) {
273 blob->offset = offset;
274 offset += (blob->size + pagemask) & ~pagemask;
275 }
276
277 /* ...and update the entries */
278 for (c = 0; c < entry_count; c++) {
279 entry = &entries[c];
280
281 entry->offset = entry->blob->offset;
282 entry->size = (entry->blob->size + pagemask) & ~pagemask;
283 }
284
285 /*
286 * All parts are now gathered, so build the dt block
287 */
288 dtbh = calloc(hdr_sz + blob_sz, 1);
289 if (dtbh == NULL)
290 err(1, "failed to allocate memory");
291
292 offset = 0;
293
294 memcpy(dtbh, DTBH_MAGIC, sizeof(uint32_t));
295 memcpy(dtbh + sizeof(uint32_t), &version, sizeof(uint32_t));
296 memcpy(dtbh + (sizeof(uint32_t) * 2), &entry_count, sizeof(uint32_t));
297
298 offset += DT_HEADER_PHYS_SIZE;
299
300 /* add dtbh entries */
301 for (c = 0; c < entry_count; c++) {
302 entry = &entries[c];
303 memcpy(dtbh + offset, entry, DT_ENTRY_PHYS_SIZE);
304 offset += DT_ENTRY_PHYS_SIZE;
305 }
306
307 /* add padding after dt header */
308 offset += pagesize - (offset & pagemask);
309
310 for (blob = blob_list; blob; blob = blob->next) {
311 memcpy(dtbh + offset, blob->payload, blob->size);
312 offset += (blob->size + pagemask) & ~pagemask;
313 }
314
315 *_sz = hdr_sz + blob_sz;
316
317 return dtbh;
318}
319
Ketut Putu Kumajaya63846372014-06-23 20:05:33 +0700320int usage(void)
321{
322 fprintf(stderr,"usage: mkbootimg\n"
323 " --kernel <filename>\n"
324 " --ramdisk <filename>\n"
325 " [ --second <2ndbootloader-filename> ]\n"
326 " [ --cmdline <kernel-commandline> ]\n"
327 " [ --board <boardname> ]\n"
328 " [ --base <address> ]\n"
329 " [ --pagesize <pagesize> ]\n"
330 " [ --ramdisk_offset <address> ]\n"
Ketut Putu Kumajaya72b1c492014-06-24 16:18:20 +0700331 " [ --dt_dir <dtb path> ]\n"
Ketut Putu Kumajaya63846372014-06-23 20:05:33 +0700332 " [ --dt <filename> ]\n"
Ketut Putu Kumajaya0c2cc5a2014-06-23 20:18:46 +0700333 " [ --signature <filename> ]\n"
Ketut Putu Kumajaya63846372014-06-23 20:05:33 +0700334 " -o|--output <filename>\n"
335 );
336 return 1;
337}
338
339
340
341static unsigned char padding[131072] = { 0, };
342
343int write_padding(int fd, unsigned pagesize, unsigned itemsize)
344{
345 unsigned pagemask = pagesize - 1;
346 unsigned count;
347
348 if((itemsize & pagemask) == 0) {
349 return 0;
350 }
351
352 count = pagesize - (itemsize & pagemask);
353
354 if(write(fd, padding, count) != count) {
355 return -1;
356 } else {
357 return 0;
358 }
359}
360
361int main(int argc, char **argv)
362{
363 boot_img_hdr hdr;
364
365 char *kernel_fn = 0;
366 void *kernel_data = 0;
367 char *ramdisk_fn = 0;
368 void *ramdisk_data = 0;
369 char *second_fn = 0;
370 void *second_data = 0;
371 char *cmdline = "";
372 char *bootimg = 0;
373 char *board = "";
Ketut Putu Kumajaya72b1c492014-06-24 16:18:20 +0700374 char *dt_dir = 0;
Ketut Putu Kumajaya63846372014-06-23 20:05:33 +0700375 char *dt_fn = 0;
376 void *dt_data = 0;
Ketut Putu Kumajaya0c2cc5a2014-06-23 20:18:46 +0700377 char *sig_fn = 0;
378 void *sig_data = 0;
Ketut Putu Kumajaya63846372014-06-23 20:05:33 +0700379 unsigned pagesize = 2048;
380 int fd;
381 SHA_CTX ctx;
382 uint8_t* sha;
383 unsigned base = 0x10000000;
384 unsigned kernel_offset = 0x00008000;
385 unsigned ramdisk_offset = 0x01000000;
386 unsigned second_offset = 0x00f00000;
387 unsigned tags_offset = 0x00000100;
388
389 argc--;
390 argv++;
391
392 memset(&hdr, 0, sizeof(hdr));
393
394 while(argc > 0){
395 char *arg = argv[0];
396 char *val = argv[1];
397 if(argc < 2) {
398 return usage();
399 }
400 argc -= 2;
401 argv += 2;
402 if(!strcmp(arg, "--output") || !strcmp(arg, "-o")) {
403 bootimg = val;
404 } else if(!strcmp(arg, "--kernel")) {
405 kernel_fn = val;
406 } else if(!strcmp(arg, "--ramdisk")) {
407 ramdisk_fn = val;
408 } else if(!strcmp(arg, "--second")) {
409 second_fn = val;
410 } else if(!strcmp(arg, "--cmdline")) {
411 cmdline = val;
412 } else if(!strcmp(arg, "--base")) {
413 base = strtoul(val, 0, 16);
414 } else if(!strcmp(arg, "--kernel_offset")) {
415 kernel_offset = strtoul(val, 0, 16);
416 } else if(!strcmp(arg, "--ramdisk_offset")) {
417 ramdisk_offset = strtoul(val, 0, 16);
418 } else if(!strcmp(arg, "--second_offset")) {
419 second_offset = strtoul(val, 0, 16);
420 } else if(!strcmp(arg, "--tags_offset")) {
421 tags_offset = strtoul(val, 0, 16);
422 } else if(!strcmp(arg, "--board")) {
423 board = val;
424 } else if(!strcmp(arg,"--pagesize")) {
425 pagesize = strtoul(val, 0, 10);
426 if ((pagesize != 2048) && (pagesize != 4096) && (pagesize != 8192) && (pagesize != 16384) && (pagesize != 32768) && (pagesize != 65536) && (pagesize != 131072)) {
427 fprintf(stderr,"error: unsupported page size %d\n", pagesize);
428 return -1;
429 }
Ketut Putu Kumajaya72b1c492014-06-24 16:18:20 +0700430 } else if (!strcmp(arg, "--dt_dir")) {
431 dt_dir = val;
Ketut Putu Kumajaya63846372014-06-23 20:05:33 +0700432 } else if(!strcmp(arg, "--dt")) {
433 dt_fn = val;
Ketut Putu Kumajaya0c2cc5a2014-06-23 20:18:46 +0700434 } else if(!strcmp(arg, "--signature")) {
435 sig_fn = val;
Ketut Putu Kumajaya63846372014-06-23 20:05:33 +0700436 } else {
437 return usage();
438 }
439 }
440 hdr.page_size = pagesize;
441
442 hdr.kernel_addr = base + kernel_offset;
443 hdr.ramdisk_addr = base + ramdisk_offset;
444 hdr.second_addr = base + second_offset;
445 hdr.tags_addr = base + tags_offset;
446
Ketut Putu Kumajaya72b1c492014-06-24 16:18:20 +0700447 if (dt_dir && dt_fn) {
448 fprintf(stderr,"error: don't use both --dt_dir and --dt option\n");
449 return usage();
450 }
451
Ketut Putu Kumajaya63846372014-06-23 20:05:33 +0700452 if(bootimg == 0) {
453 fprintf(stderr,"error: no output filename specified\n");
454 return usage();
455 }
456
457 if(kernel_fn == 0) {
458 fprintf(stderr,"error: no kernel image specified\n");
459 return usage();
460 }
461
462 if(ramdisk_fn == 0) {
463 fprintf(stderr,"error: no ramdisk image specified\n");
464 return usage();
465 }
466
467 if(strlen(board) >= BOOT_NAME_SIZE) {
468 fprintf(stderr,"error: board name too large\n");
469 return usage();
470 }
471
472 strcpy(hdr.name, board);
473
474 memcpy(hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
475
476 if(strlen(cmdline) > (BOOT_ARGS_SIZE - 1)) {
477 fprintf(stderr,"error: kernel commandline too large\n");
478 return 1;
479 }
480 strcpy((char*)hdr.cmdline, cmdline);
481
482 kernel_data = load_file(kernel_fn, &hdr.kernel_size);
483 if(kernel_data == 0) {
484 fprintf(stderr,"error: could not load kernel '%s'\n", kernel_fn);
485 return 1;
486 }
487
488 if(!strcmp(ramdisk_fn,"NONE")) {
489 ramdisk_data = 0;
490 hdr.ramdisk_size = 0;
491 } else {
492 ramdisk_data = load_file(ramdisk_fn, &hdr.ramdisk_size);
493 if(ramdisk_data == 0) {
494 fprintf(stderr,"error: could not load ramdisk '%s'\n", ramdisk_fn);
495 return 1;
496 }
497 }
498
499 if(second_fn) {
500 second_data = load_file(second_fn, &hdr.second_size);
501 if(second_data == 0) {
502 fprintf(stderr,"error: could not load secondstage '%s'\n", second_fn);
503 return 1;
504 }
505 }
506
Ketut Putu Kumajaya72b1c492014-06-24 16:18:20 +0700507 if (dt_dir) {
508 dt_data = load_dtbh_block(dt_dir, pagesize, &hdr.dt_size);
509 if (dt_data == 0) {
510 fprintf(stderr, "error: could not load device tree blobs '%s'\n", dt_dir);
511 return 1;
512 }
513 }
514
Ketut Putu Kumajaya63846372014-06-23 20:05:33 +0700515 if(dt_fn) {
516 dt_data = load_file(dt_fn, &hdr.dt_size);
517 if (dt_data == 0) {
518 fprintf(stderr,"error: could not load device tree image '%s'\n", dt_fn);
519 return 1;
520 }
521 }
522
Ketut Putu Kumajaya0c2cc5a2014-06-23 20:18:46 +0700523 if(sig_fn) {
524 sig_data = load_file(sig_fn, 0);
525 if (sig_data == 0) {
526 fprintf(stderr,"error: could not load signature '%s'\n", sig_fn);
527 return 1;
528 }
529 }
530
Ketut Putu Kumajaya63846372014-06-23 20:05:33 +0700531 /* put a hash of the contents in the header so boot images can be
532 * differentiated based on their first 2k.
533 */
534 SHA_init(&ctx);
535 SHA_update(&ctx, kernel_data, hdr.kernel_size);
536 SHA_update(&ctx, &hdr.kernel_size, sizeof(hdr.kernel_size));
537 SHA_update(&ctx, ramdisk_data, hdr.ramdisk_size);
538 SHA_update(&ctx, &hdr.ramdisk_size, sizeof(hdr.ramdisk_size));
539 SHA_update(&ctx, second_data, hdr.second_size);
540 SHA_update(&ctx, &hdr.second_size, sizeof(hdr.second_size));
541 if(dt_data) {
542 SHA_update(&ctx, dt_data, hdr.dt_size);
543 SHA_update(&ctx, &hdr.dt_size, sizeof(hdr.dt_size));
544 }
545 sha = SHA_final(&ctx);
546 memcpy(hdr.id, sha,
547 SHA_DIGEST_SIZE > sizeof(hdr.id) ? sizeof(hdr.id) : SHA_DIGEST_SIZE);
548
549 fd = open(bootimg, O_CREAT | O_TRUNC | O_WRONLY, 0644);
550 if(fd < 0) {
551 fprintf(stderr,"error: could not create '%s'\n", bootimg);
552 return 1;
553 }
554
555 if(write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) goto fail;
556 if(write_padding(fd, pagesize, sizeof(hdr))) goto fail;
557
558 if(write(fd, kernel_data, hdr.kernel_size) != hdr.kernel_size) goto fail;
559 if(write_padding(fd, pagesize, hdr.kernel_size)) goto fail;
560
561 if(write(fd, ramdisk_data, hdr.ramdisk_size) != hdr.ramdisk_size) goto fail;
562 if(write_padding(fd, pagesize, hdr.ramdisk_size)) goto fail;
563
564 if(second_data) {
565 if(write(fd, second_data, hdr.second_size) != hdr.second_size) goto fail;
566 if(write_padding(fd, pagesize, hdr.ramdisk_size)) goto fail;
567 }
568
569 if(dt_data) {
570 if(write(fd, dt_data, hdr.dt_size) != hdr.dt_size) goto fail;
571 if(write_padding(fd, pagesize, hdr.dt_size)) goto fail;
572 }
Ketut Putu Kumajaya0c2cc5a2014-06-23 20:18:46 +0700573
574 if(sig_data) {
575 if(write(fd, sig_data, 256) != 256) goto fail;
576 }
577
Ketut Putu Kumajaya63846372014-06-23 20:05:33 +0700578 return 0;
579
580fail:
581 unlink(bootimg);
582 close(fd);
583 fprintf(stderr,"error: failed writing '%s': %s\n", bootimg,
584 strerror(errno));
585 return 1;
586}