1 /*
2 * Copyright 2014 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6 #include <errno.h>
7 #include <getopt.h>
8 #include <inttypes.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17
18 #include "futility.h"
19 #include "gbb_header.h"
20
print_help(const char * prog)21 static void print_help(const char *prog)
22 {
23 printf("\n"
24 "Usage: " MYNAME " %s [-g|-s|-c] [OPTIONS] "
25 "bios_file [output_file]\n"
26 "\n"
27 "GET MODE:\n"
28 "-g, --get (default)\tGet (read) from bios_file, "
29 "with following options:\n"
30 " --hwid \tReport hardware id (default).\n"
31 " --flags \tReport header flags.\n"
32 " --digest \tReport digest of hwid (>= v1.2)\n"
33 " -k, --rootkey=FILE \tFile name to export Root Key.\n"
34 " -b, --bmpfv=FILE \tFile name to export Bitmap FV.\n"
35 " -r --recoverykey=FILE\tFile name to export Recovery Key.\n"
36 "\n"
37 "SET MODE:\n"
38 "-s, --set \tSet (write) to bios_file, "
39 "with following options:\n"
40 " -o, --output=FILE \tNew file name for ouptput.\n"
41 " --hwid=HWID \tThe new hardware id to be changed.\n"
42 " --flags=FLAGS \tThe new (numeric) flags value.\n"
43 " -k, --rootkey=FILE \tFile name of new Root Key.\n"
44 " -b, --bmpfv=FILE \tFile name of new Bitmap FV.\n"
45 " -r --recoverykey=FILE\tFile name of new Recovery Key.\n"
46 "\n"
47 "CREATE MODE:\n"
48 "-c, --create=hwid_size,rootkey_size,bmpfv_size,"
49 "recoverykey_size\n"
50 " \tCreate a GBB blob by given size list.\n"
51 "SAMPLE:\n"
52 " %s -g bios.bin\n"
53 " %s --set --hwid='New Model' -k key.bin"
54 " bios.bin newbios.bin\n"
55 " %s -c 0x100,0x1000,0x03DE80,0x1000 gbb.blob\n\n",
56 prog, prog, prog, prog);
57 }
58
59 enum {
60 OPT_HWID = 1000,
61 OPT_FLAGS,
62 OPT_DIGEST,
63 };
64
65 /* Command line options */
66 static struct option long_opts[] = {
67 /* name has_arg *flag val */
68 {"get", 0, NULL, 'g'},
69 {"set", 0, NULL, 's'},
70 {"create", 1, NULL, 'c'},
71 {"output", 1, NULL, 'o'},
72 {"rootkey", 1, NULL, 'k'},
73 {"bmpfv", 1, NULL, 'b'},
74 {"recoverykey", 1, NULL, 'r'},
75 {"hwid", 0, NULL, OPT_HWID},
76 {"flags", 0, NULL, OPT_FLAGS},
77 {"digest", 0, NULL, OPT_DIGEST},
78 {NULL, 0, NULL, 0},
79 };
80
81 static char *short_opts = ":gsc:o:k:b:r:";
82
83 /* Change the has_arg field of a long_opts entry */
opt_has_arg(const char * name,int val)84 static void opt_has_arg(const char *name, int val)
85 {
86 struct option *p;
87 for (p = long_opts; p->name; p++)
88 if (!strcmp(name, p->name)) {
89 p->has_arg = val;
90 break;
91 }
92 }
93
94 static int errorcnt;
95
96 #define GBB_SEARCH_STRIDE 4
FindGbbHeader(uint8_t * ptr,size_t size)97 static GoogleBinaryBlockHeader *FindGbbHeader(uint8_t *ptr, size_t size)
98 {
99 size_t i;
100 GoogleBinaryBlockHeader *tmp, *gbb_header = NULL;
101 int count = 0;
102
103 for (i = 0; i <= size - GBB_SEARCH_STRIDE; i += GBB_SEARCH_STRIDE) {
104 if (0 != memcmp(ptr + i, GBB_SIGNATURE, GBB_SIGNATURE_SIZE))
105 continue;
106
107 /* Found something. See if it's any good. */
108 tmp = (GoogleBinaryBlockHeader *) (ptr + i);
109 if (futil_valid_gbb_header(tmp, size - i, NULL))
110 if (!count++)
111 gbb_header = tmp;
112 }
113
114 switch (count) {
115 case 0:
116 errorcnt++;
117 return NULL;
118 case 1:
119 return gbb_header;
120 default:
121 fprintf(stderr, "ERROR: multiple GBB headers found\n");
122 errorcnt++;
123 return NULL;
124 }
125 }
126
create_gbb(const char * desc,off_t * sizeptr)127 static uint8_t *create_gbb(const char *desc, off_t *sizeptr)
128 {
129 char *str, *sizes, *param, *e = NULL;
130 size_t size = GBB_HEADER_SIZE;
131 int i = 0;
132 /* Danger Will Robinson! four entries ==> four paramater blocks */
133 uint32_t val[] = { 0, 0, 0, 0 };
134 uint8_t *buf;
135 GoogleBinaryBlockHeader *gbb;
136
137 sizes = strdup(desc);
138 if (!sizes) {
139 errorcnt++;
140 fprintf(stderr, "ERROR: strdup() failed: %s\n",
141 strerror(errno));
142 return NULL;
143 }
144
145 for (str = sizes; (param = strtok(str, ", ")) != NULL; str = NULL) {
146 val[i] = (uint32_t) strtoul(param, &e, 0);
147 if (e && *e) {
148 errorcnt++;
149 fprintf(stderr,
150 "ERROR: invalid creation parameter: \"%s\"\n",
151 param);
152 free(sizes);
153 return NULL;
154 }
155 size += val[i++];
156 if (i > ARRAY_SIZE(val))
157 break;
158 }
159
160 buf = (uint8_t *) calloc(1, size);
161 if (!buf) {
162 errorcnt++;
163 fprintf(stderr, "ERROR: can't malloc %zu bytes: %s\n",
164 size, strerror(errno));
165 free(sizes);
166 return NULL;
167 } else if (sizeptr) {
168 *sizeptr = size;
169 }
170
171 gbb = (GoogleBinaryBlockHeader *) buf;
172 memcpy(gbb->signature, GBB_SIGNATURE, GBB_SIGNATURE_SIZE);
173 gbb->major_version = GBB_MAJOR_VER;
174 gbb->minor_version = GBB_MINOR_VER;
175 gbb->header_size = GBB_HEADER_SIZE;
176 gbb->flags = 0;
177
178 i = GBB_HEADER_SIZE;
179 gbb->hwid_offset = i;
180 gbb->hwid_size = val[0];
181 i += val[0];
182
183 gbb->rootkey_offset = i;
184 gbb->rootkey_size = val[1];
185 i += val[1];
186
187 gbb->bmpfv_offset = i;
188 gbb->bmpfv_size = val[2];
189 i += val[2];
190
191 gbb->recovery_key_offset = i;
192 gbb->recovery_key_size = val[3];
193 i += val[1];
194
195 free(sizes);
196 return buf;
197 }
198
read_entire_file(const char * filename,off_t * sizeptr)199 static uint8_t *read_entire_file(const char *filename, off_t *sizeptr)
200 {
201 FILE *fp = NULL;
202 uint8_t *buf = NULL;
203 struct stat sb;
204
205 fp = fopen(filename, "rb");
206 if (!fp) {
207 fprintf(stderr, "ERROR: Unable to open %s for reading: %s\n",
208 filename, strerror(errno));
209 goto fail;
210 }
211
212 if (0 != fstat(fileno(fp), &sb)) {
213 fprintf(stderr, "ERROR: can't fstat %s: %s\n",
214 filename, strerror(errno));
215 goto fail;
216 }
217 if (sizeptr)
218 *sizeptr = sb.st_size;
219
220 buf = (uint8_t *) malloc(sb.st_size);
221 if (!buf) {
222 fprintf(stderr, "ERROR: can't malloc %" PRIi64 " bytes: %s\n",
223 sb.st_size, strerror(errno));
224 goto fail;
225 }
226
227 if (1 != fread(buf, sb.st_size, 1, fp)) {
228 fprintf(stderr, "ERROR: Unable to read from %s: %s\n",
229 filename, strerror(errno));
230 goto fail;
231 }
232
233 if (fp && 0 != fclose(fp)) {
234 fprintf(stderr, "ERROR: Unable to close %s: %s\n",
235 filename, strerror(errno));
236 goto fail;
237 }
238
239 return buf;
240
241 fail:
242 errorcnt++;
243
244 if (buf)
245 free(buf);
246
247 if (fp && 0 != fclose(fp))
248 fprintf(stderr, "ERROR: Unable to close %s: %s\n",
249 filename, strerror(errno));
250 return NULL;
251 }
252
write_to_file(const char * msg,const char * filename,uint8_t * start,size_t size)253 static int write_to_file(const char *msg, const char *filename,
254 uint8_t *start, size_t size)
255 {
256 FILE *fp;
257 int r = 0;
258
259 fp = fopen(filename, "wb");
260 if (!fp) {
261 fprintf(stderr, "ERROR: Unable to open %s for writing: %s\n",
262 filename, strerror(errno));
263 errorcnt++;
264 return errno;
265 }
266
267 /* Don't write zero bytes */
268 if (size && 1 != fwrite(start, size, 1, fp)) {
269 fprintf(stderr, "ERROR: Unable to write to %s: %s\n",
270 filename, strerror(errno));
271 errorcnt++;
272 r = errno;
273 }
274
275 if (0 != fclose(fp)) {
276 fprintf(stderr, "ERROR: Unable to close %s: %s\n",
277 filename, strerror(errno));
278 errorcnt++;
279 if (!r)
280 r = errno;
281 }
282
283 if (!r && msg)
284 printf("%s %s\n", msg, filename);
285
286 return r;
287 }
288
read_from_file(const char * msg,const char * filename,uint8_t * start,uint32_t size)289 static int read_from_file(const char *msg, const char *filename,
290 uint8_t *start, uint32_t size)
291 {
292 FILE *fp;
293 struct stat sb;
294 size_t count;
295 int r = 0;
296
297 fp = fopen(filename, "rb");
298 if (!fp) {
299 fprintf(stderr, "ERROR: Unable to open %s for reading: %s\n",
300 filename, strerror(errno));
301 errorcnt++;
302 return errno;
303 }
304
305 if (0 != fstat(fileno(fp), &sb)) {
306 fprintf(stderr, "ERROR: can't fstat %s: %s\n",
307 filename, strerror(errno));
308 errorcnt++;
309 r = errno;
310 goto done_close;
311 }
312
313 if (sb.st_size > size) {
314 fprintf(stderr,
315 "ERROR: file %s exceeds capacity (%" PRIu32 ")\n",
316 filename, size);
317 errorcnt++;
318 r = errno;
319 goto done_close;
320 }
321
322 /* Wipe existing data. */
323 memset(start, 0, size);
324
325 /* It's okay if we read less than size. That's just the max. */
326 count = fread(start, 1, size, fp);
327 if (ferror(fp)) {
328 fprintf(stderr,
329 "ERROR: Read %zu/%" PRIi64 " bytes from %s: %s\n",
330 count, sb.st_size, filename, strerror(errno));
331 errorcnt++;
332 r = errno;
333 }
334
335 done_close:
336 if (0 != fclose(fp)) {
337 fprintf(stderr, "ERROR: Unable to close %s: %s\n",
338 filename, strerror(errno));
339 errorcnt++;
340 if (!r)
341 r = errno;
342 }
343
344 if (!r && msg)
345 printf(" - import %s from %s: success\n", msg, filename);
346
347 return r;
348 }
349
do_gbb_utility(int argc,char * argv[])350 static int do_gbb_utility(int argc, char *argv[])
351 {
352 enum do_what_now { DO_GET, DO_SET, DO_CREATE } mode = DO_GET;
353 char *infile = NULL;
354 char *outfile = NULL;
355 char *opt_create = NULL;
356 char *opt_rootkey = NULL;
357 char *opt_bmpfv = NULL;
358 char *opt_recoverykey = NULL;
359 char *opt_hwid = NULL;
360 char *opt_flags = NULL;
361 int sel_hwid = 0;
362 int sel_digest = 0;
363 int sel_flags = 0;
364 uint8_t *inbuf = NULL;
365 off_t filesize;
366 uint8_t *outbuf = NULL;
367 GoogleBinaryBlockHeader *gbb;
368 uint8_t *gbb_base;
369 int i;
370
371 opterr = 0; /* quiet, you */
372 while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
373 switch (i) {
374 case 'g':
375 mode = DO_GET;
376 opt_has_arg("flags", 0);
377 opt_has_arg("hwid", 0);
378 break;
379 case 's':
380 mode = DO_SET;
381 opt_has_arg("flags", 1);
382 opt_has_arg("hwid", 1);
383 break;
384 case 'c':
385 mode = DO_CREATE;
386 opt_create = optarg;
387 break;
388 case 'o':
389 outfile = optarg;
390 break;
391 case 'k':
392 opt_rootkey = optarg;
393 break;
394 case 'b':
395 opt_bmpfv = optarg;
396 break;
397 case 'r':
398 opt_recoverykey = optarg;
399 break;
400 case OPT_HWID:
401 /* --hwid is optional: null might be okay */
402 opt_hwid = optarg;
403 sel_hwid = 1;
404 break;
405 case OPT_FLAGS:
406 /* --flags is optional: null might be okay */
407 opt_flags = optarg;
408 sel_flags = 1;
409 break;
410 case OPT_DIGEST:
411 sel_digest = 1;
412 break;
413 case '?':
414 errorcnt++;
415 if (optopt)
416 fprintf(stderr,
417 "ERROR: unrecognized option: -%c\n",
418 optopt);
419 else if (argv[optind - 1])
420 fprintf(stderr,
421 "ERROR: unrecognized option "
422 "(possibly \"%s\")\n",
423 argv[optind - 1]);
424 else
425 fprintf(stderr, "ERROR: unrecognized option\n");
426 break;
427 case ':':
428 errorcnt++;
429 if (argv[optind - 1])
430 fprintf(stderr,
431 "ERROR: missing argument to -%c (%s)\n",
432 optopt, argv[optind - 1]);
433 else
434 fprintf(stderr,
435 "ERROR: missing argument to -%c\n",
436 optopt);
437 break;
438 default:
439 errorcnt++;
440 fprintf(stderr,
441 "ERROR: error while parsing options\n");
442 }
443 }
444
445 /* Problems? */
446 if (errorcnt) {
447 print_help(argv[0]);
448 return 1;
449 }
450
451 /* Now try to do something */
452 switch (mode) {
453 case DO_GET:
454 if (argc - optind < 1) {
455 fprintf(stderr, "\nERROR: missing input filename\n");
456 print_help(argv[0]);
457 return 1;
458 } else {
459 infile = argv[optind++];
460 }
461
462 /* With no args, show the HWID */
463 if (!opt_rootkey && !opt_bmpfv && !opt_recoverykey
464 && !sel_flags && !sel_digest)
465 sel_hwid = 1;
466
467 inbuf = read_entire_file(infile, &filesize);
468 if (!inbuf)
469 break;
470
471 gbb = FindGbbHeader(inbuf, filesize);
472 if (!gbb) {
473 fprintf(stderr, "ERROR: No GBB found in %s\n", infile);
474 break;
475 }
476 gbb_base = (uint8_t *) gbb;
477
478 /* Get the stuff */
479 if (sel_hwid)
480 printf("hardware_id: %s\n",
481 gbb->hwid_size ? (char *)(gbb_base +
482 gbb->
483 hwid_offset) : "");
484 if (sel_digest)
485 print_hwid_digest(gbb, "digest: ", "\n");
486
487 if (sel_flags)
488 printf("flags: 0x%08x\n", gbb->flags);
489 if (opt_rootkey)
490 write_to_file(" - exported root_key to file:",
491 opt_rootkey,
492 gbb_base + gbb->rootkey_offset,
493 gbb->rootkey_size);
494 if (opt_bmpfv)
495 write_to_file(" - exported bmp_fv to file:", opt_bmpfv,
496 gbb_base + gbb->bmpfv_offset,
497 gbb->bmpfv_size);
498 if (opt_recoverykey)
499 write_to_file(" - exported recovery_key to file:",
500 opt_recoverykey,
501 gbb_base + gbb->recovery_key_offset,
502 gbb->recovery_key_size);
503 break;
504
505 case DO_SET:
506 if (argc - optind < 1) {
507 fprintf(stderr, "\nERROR: missing input filename\n");
508 print_help(argv[0]);
509 return 1;
510 }
511 infile = argv[optind++];
512 if (!outfile)
513 outfile = (argc - optind < 1) ? infile : argv[optind++];
514
515 if (sel_hwid && !opt_hwid) {
516 fprintf(stderr, "\nERROR: missing new HWID value\n");
517 print_help(argv[0]);
518 return 1;
519 }
520 if (sel_flags && (!opt_flags || !*opt_flags)) {
521 fprintf(stderr, "\nERROR: missing new flags value\n");
522 print_help(argv[0]);
523 return 1;
524 }
525
526 /* With no args, we'll either copy it unchanged or do nothing */
527 inbuf = read_entire_file(infile, &filesize);
528 if (!inbuf)
529 break;
530
531 gbb = FindGbbHeader(inbuf, filesize);
532 if (!gbb) {
533 fprintf(stderr, "ERROR: No GBB found in %s\n", infile);
534 break;
535 }
536 gbb_base = (uint8_t *) gbb;
537
538 outbuf = (uint8_t *) malloc(filesize);
539 if (!outbuf) {
540 errorcnt++;
541 fprintf(stderr,
542 "ERROR: can't malloc %" PRIi64 " bytes: %s\n",
543 filesize, strerror(errno));
544 break;
545 }
546
547 /* Switch pointers to outbuf */
548 memcpy(outbuf, inbuf, filesize);
549 gbb = FindGbbHeader(outbuf, filesize);
550 if (!gbb) {
551 fprintf(stderr,
552 "INTERNAL ERROR: No GBB found in outbuf\n");
553 exit(1);
554 }
555 gbb_base = (uint8_t *) gbb;
556
557 if (opt_hwid) {
558 if (strlen(opt_hwid) + 1 > gbb->hwid_size) {
559 fprintf(stderr,
560 "ERROR: null-terminated HWID"
561 " exceeds capacity (%d)\n",
562 gbb->hwid_size);
563 errorcnt++;
564 } else {
565 /* Wipe data before writing new value. */
566 memset(gbb_base + gbb->hwid_offset, 0,
567 gbb->hwid_size);
568 strcpy((char *)(gbb_base + gbb->hwid_offset),
569 opt_hwid);
570 update_hwid_digest(gbb);
571 }
572 }
573
574 if (opt_flags) {
575 char *e = NULL;
576 uint32_t val;
577 val = (uint32_t) strtoul(opt_flags, &e, 0);
578 if (e && *e) {
579 fprintf(stderr,
580 "ERROR: invalid flags value: %s\n",
581 opt_flags);
582 errorcnt++;
583 } else {
584 gbb->flags = val;
585 }
586 }
587
588 if (opt_rootkey)
589 read_from_file("root_key", opt_rootkey,
590 gbb_base + gbb->rootkey_offset,
591 gbb->rootkey_size);
592 if (opt_bmpfv)
593 read_from_file("bmp_fv", opt_bmpfv,
594 gbb_base + gbb->bmpfv_offset,
595 gbb->bmpfv_size);
596 if (opt_recoverykey)
597 read_from_file("recovery_key", opt_recoverykey,
598 gbb_base + gbb->recovery_key_offset,
599 gbb->recovery_key_size);
600
601 /* Write it out if there are no problems. */
602 if (!errorcnt)
603 write_to_file("successfully saved new image to:",
604 outfile, outbuf, filesize);
605
606 break;
607
608 case DO_CREATE:
609 if (!outfile) {
610 if (argc - optind < 1) {
611 fprintf(stderr,
612 "\nERROR: missing output filename\n");
613 print_help(argv[0]);
614 return 1;
615 }
616 outfile = argv[optind++];
617 }
618 /* Parse the creation args */
619 outbuf = create_gbb(opt_create, &filesize);
620 if (!outbuf) {
621 fprintf(stderr,
622 "\nERROR: unable to parse creation spec (%s)\n",
623 opt_create);
624 print_help(argv[0]);
625 return 1;
626 }
627 if (!errorcnt)
628 write_to_file("successfully created new GBB to:",
629 outfile, outbuf, filesize);
630 break;
631 }
632
633 if (inbuf)
634 free(inbuf);
635 if (outbuf)
636 free(outbuf);
637 return !!errorcnt;
638 }
639
640 DECLARE_FUTIL_COMMAND(gbb_utility, do_gbb_utility,
641 VBOOT_VERSION_ALL,
642 "Manipulate the Google Binary Block (GBB)",
643 print_help);
644