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