1 /* Copyright 2014 The ChromiumOS Authors
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
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 "flash_helpers.h"
19 #include "futility.h"
20 #include "updater.h"
21 #include "updater_utils.h"
22 #include "2gbb_flags.h"
23
24 #ifdef USE_FLASHROM
25 #define FLASH_ARG_HELP \
26 " --flash \tRead from and write to flash" \
27 ", ignore file arguments.\n"
28 #define FLASH_MORE_HELP \
29 "In GET and SET mode, the following options modify the " \
30 "behaviour of flashing. Presence of any of these implies " \
31 "--flash.\n" \
32 SHARED_FLASH_ARGS_HELP \
33 "\n"
34 #else
35 #define FLASH_ARG_HELP
36 #define FLASH_MORE_HELP
37 #endif /* USE_FLASHROM */
38
print_help(int argc,char * argv[])39 static void print_help(int argc, char *argv[])
40 {
41 printf("\n"
42 "Usage: " MYNAME " %s [-g|-s|-c] [OPTIONS] "
43 "[image_file] [output_file]\n"
44 "\n"
45 "GET MODE:\n"
46 "-g, --get (default)\tGet (read) from image_file or flash, "
47 "with following options:\n"
48 FLASH_ARG_HELP
49 " --hwid \tReport hardware id (default).\n"
50 " --flags \tReport header flags.\n"
51 " --digest \tReport digest of hwid (>= v1.2)\n"
52 " -k, --rootkey=FILE \tFile name to export Root Key.\n"
53 " -b, --bmpfv=FILE \tFile name to export Bitmap FV.\n"
54 " -r --recoverykey=FILE\tFile name to export Recovery Key.\n"
55 " -e --explicit \tReport header flags by name.\n"
56 "\n"
57 "SET MODE:\n"
58 "-s, --set \tSet (write) to flash or file, "
59 "with following options:\n"
60 FLASH_ARG_HELP
61 " -o, --output=FILE \tNew file name for ouptput.\n"
62 " --hwid=HWID \tThe new hardware id to be changed.\n"
63 " --flags=FLAGS \tThe new (numeric) flags value or +/- diff value.\n"
64 " -k, --rootkey=FILE \tFile name of new Root Key.\n"
65 " -b, --bmpfv=FILE \tFile name of new Bitmap FV.\n"
66 " -r --recoverykey=FILE\tFile name of new Recovery Key.\n"
67 "\n"
68 "CREATE MODE:\n"
69 "-c, --create=hwid_size,rootkey_size,bmpfv_size,"
70 "recoverykey_size\n"
71 " \tCreate a GBB blob by given size list.\n\n"
72 FLASH_MORE_HELP
73 "SAMPLE:\n"
74 " %s -g image.bin\n"
75 " %s --set --hwid='New Model' -k key.bin"
76 " image.bin newimage.bin\n"
77 " %s -c 0x100,0x1000,0x03DE80,0x1000 gbb.blob\n\n"
78 "GBB Flags:\n"
79 " To get a developer-friendly device, try 0x18 (dev_mode boot_usb).\n"
80 " For early bringup development, try 0x40b9.\n",
81 argv[0], argv[0], argv[0], argv[0]);
82 for (vb2_gbb_flags_t flag = 1; flag; flag <<= 1) {
83 const char *name;
84 const char *description;
85 if (vb2_get_gbb_flag_description(flag, &name, &description) !=
86 VB2_SUCCESS)
87 break;
88 printf(" 0x%08x\t%s\n"
89 " \t%s\n",
90 flag, name, description);
91 }
92 }
93
94 enum {
95 OPT_HWID = 0x1000,
96 OPT_FLAGS,
97 OPT_DIGEST,
98 OPT_FLASH,
99 OPT_HELP,
100 };
101
102 /* Command line options */
103 static struct option long_opts[] = {
104 SHARED_FLASH_ARGS_LONGOPTS
105 /* name has_arg *flag val */
106 {"get", 0, NULL, 'g'},
107 {"set", 0, NULL, 's'},
108 {"create", 1, NULL, 'c'},
109 {"output", 1, NULL, 'o'},
110 {"rootkey", 1, NULL, 'k'},
111 {"bmpfv", 1, NULL, 'b'},
112 {"recoverykey", 1, NULL, 'r'},
113 {"hwid", 0, NULL, OPT_HWID},
114 {"flags", 0, NULL, OPT_FLAGS},
115 {"explicit", 0, NULL, 'e'},
116 {"digest", 0, NULL, OPT_DIGEST},
117 {"flash", 0, NULL, OPT_FLASH},
118 {"help", 0, NULL, OPT_HELP},
119 {NULL, 0, NULL, 0},
120 };
121
122 static const char *short_opts = ":gsc:o:k:b:r:e" SHARED_FLASH_ARGS_SHORTOPTS;
123
124 /* Change the has_arg field of a long_opts entry */
opt_has_arg(const char * name,int val)125 static void opt_has_arg(const char *name, int val)
126 {
127 for (struct option *p = long_opts; p->name; p++) {
128 if (!strcmp(name, p->name)) {
129 p->has_arg = val;
130 break;
131 }
132 }
133 }
134
135 #define GBB_SEARCH_STRIDE 4
FindGbbHeader(uint8_t * ptr,size_t size)136 static struct vb2_gbb_header *FindGbbHeader(uint8_t *ptr, size_t size)
137 {
138 size_t i;
139 struct vb2_gbb_header *tmp, *gbb_header = NULL;
140 int count = 0;
141
142 for (i = 0; i <= size - GBB_SEARCH_STRIDE; i += GBB_SEARCH_STRIDE) {
143 if (memcmp(ptr + i, VB2_GBB_SIGNATURE, VB2_GBB_SIGNATURE_SIZE))
144 continue;
145
146 /* Found something. See if it's any good. */
147 tmp = (struct vb2_gbb_header *) (ptr + i);
148 if (futil_valid_gbb_header(tmp, size - i, NULL))
149 if (!count++)
150 gbb_header = tmp;
151 }
152
153 switch (count) {
154 case 0:
155 return NULL;
156 case 1:
157 return gbb_header;
158 default:
159 ERROR("Multiple GBB headers found\n");
160 return NULL;
161 }
162 }
163
create_gbb(const char * desc,off_t * sizeptr)164 static uint8_t *create_gbb(const char *desc, off_t *sizeptr)
165 {
166 char *param, *e = NULL;
167 size_t size = EXPECTED_VB2_GBB_HEADER_SIZE;
168 int i = 0;
169 /* Danger Will Robinson! four entries ==> four paramater blocks */
170 uint32_t val[] = { 0, 0, 0, 0 };
171
172 char *sizes = strdup(desc);
173 if (!sizes) {
174 ERROR("strdup() failed: %s\n", strerror(errno));
175 return NULL;
176 }
177
178 for (char *str = sizes; (param = strtok(str, ", ")) != NULL; str = NULL) {
179 val[i] = (uint32_t) strtoul(param, &e, 0);
180 if (e && *e) {
181 ERROR("Invalid creation parameter: \"%s\"\n", param);
182 free(sizes);
183 return NULL;
184 }
185 size += val[i++];
186 if (i > ARRAY_SIZE(val))
187 break;
188 }
189
190 uint8_t *buf = (uint8_t *) calloc(1, size);
191 if (!buf) {
192 ERROR("Can't malloc %zu bytes: %s\n", size, strerror(errno));
193 free(sizes);
194 return NULL;
195 }
196 if (sizeptr)
197 *sizeptr = size;
198
199 struct vb2_gbb_header *gbb = (struct vb2_gbb_header *) buf;
200 memcpy(gbb->signature, VB2_GBB_SIGNATURE, VB2_GBB_SIGNATURE_SIZE);
201 gbb->major_version = VB2_GBB_MAJOR_VER;
202 gbb->minor_version = VB2_GBB_MINOR_VER;
203 gbb->header_size = EXPECTED_VB2_GBB_HEADER_SIZE;
204 gbb->flags = 0;
205
206 i = EXPECTED_VB2_GBB_HEADER_SIZE;
207 gbb->hwid_offset = i;
208 gbb->hwid_size = val[0];
209 i += val[0];
210
211 gbb->rootkey_offset = i;
212 gbb->rootkey_size = val[1];
213 i += val[1];
214
215 gbb->bmpfv_offset = i;
216 gbb->bmpfv_size = val[2];
217 i += val[2];
218
219 gbb->recovery_key_offset = i;
220 gbb->recovery_key_size = val[3];
221 i += val[1];
222
223 free(sizes);
224 return buf;
225 }
226
read_entire_file(const char * filename,off_t * sizeptr)227 static uint8_t *read_entire_file(const char *filename, off_t *sizeptr)
228 {
229 uint8_t *buf = NULL;
230 struct stat sb;
231
232 FILE *fp = fopen(filename, "rb");
233 if (!fp) {
234 ERROR("Unable to open %s for reading: %s\n", filename,
235 strerror(errno));
236 goto fail;
237 }
238
239 if (fstat(fileno(fp), &sb)) {
240 ERROR("Can't fstat %s: %s\n", filename, strerror(errno));
241 goto fail;
242 }
243 if (sizeptr)
244 *sizeptr = sb.st_size;
245
246 buf = (uint8_t *) malloc(sb.st_size);
247 if (!buf) {
248 ERROR("Can't malloc %" PRIi64 " bytes: %s\n", sb.st_size,
249 strerror(errno));
250 goto fail;
251 }
252
253 if (1 != fread(buf, sb.st_size, 1, fp)) {
254 ERROR("Unable to read from %s: %s\n", filename,
255 strerror(errno));
256 goto fail;
257 }
258
259 if (fclose(fp)) {
260 ERROR("Unable to close %s: %s\n", filename, strerror(errno));
261 fp = NULL; /* Don't try to close it again */
262 goto fail;
263 }
264
265 return buf;
266
267 fail:
268 if (buf)
269 free(buf);
270
271 if (fp && fclose(fp))
272 ERROR("Unable to close %s: %s\n", filename, strerror(errno));
273 return NULL;
274 }
275
read_from_file(const char * msg,const char * filename,uint8_t * start,uint32_t size)276 static int read_from_file(const char *msg, const char *filename,
277 uint8_t *start, uint32_t size)
278 {
279 struct stat sb;
280 size_t count;
281 int r = 0;
282
283 FILE *fp = fopen(filename, "rb");
284 if (!fp) {
285 r = errno;
286 ERROR("Unable to open %s for reading: %s\n", filename, strerror(r));
287 return r;
288 }
289
290 if (fstat(fileno(fp), &sb)) {
291 r = errno;
292 ERROR("Can't fstat %s: %s\n", filename, strerror(r));
293 goto done_close;
294 }
295
296 if (sb.st_size > size) {
297 ERROR("File %s exceeds capacity (%" PRIu32 ")\n", filename, size);
298 r = -1;
299 goto done_close;
300 }
301
302 /* Wipe existing data. */
303 memset(start, 0, size);
304
305 /* It's okay if we read less than size. That's just the max. */
306 count = fread(start, 1, size, fp);
307 if (ferror(fp)) {
308 r = errno;
309 ERROR("Read %zu/%" PRIi64 " bytes from %s: %s\n", count,
310 sb.st_size, filename, strerror(r));
311 }
312
313 done_close:
314 if (fclose(fp)) {
315 int e = errno;
316 ERROR("Unable to close %s: %s\n", filename, strerror(e));
317 if (!r)
318 r = e;
319 }
320
321 if (!r && msg)
322 printf(" - import %s from %s: success\n", msg, filename);
323
324 return r;
325 }
326
327 /* Read firmware from flash. */
read_from_flash(struct updater_config * cfg,off_t * filesize)328 static uint8_t *read_from_flash(struct updater_config *cfg, off_t *filesize)
329 {
330 #ifdef USE_FLASHROM
331 /*
332 * Read the FMAP region as well, so that a subsequet write won't
333 * require another read of FMAP.
334 */
335 const char * const regions[] = {FMAP_RO_FMAP, FMAP_RO_GBB};
336 if (flashrom_read_image(&cfg->image_current, regions,
337 ARRAY_SIZE(regions), cfg->verbosity + 1))
338 return NULL;
339 uint8_t *ret = cfg->image_current.data;
340 cfg->image_current.data = NULL;
341 *filesize = cfg->image_current.size;
342 cfg->image_current.size = 0;
343 return ret;
344 #else
345 return NULL;
346 #endif /* USE_FLASHROM */
347 }
348
349 /* Write firmware to flash. Takes ownership of inbuf and outbuf data. */
write_to_flash(struct updater_config * cfg,uint8_t * outbuf,off_t filesize)350 static int write_to_flash(struct updater_config *cfg, uint8_t *outbuf,
351 off_t filesize)
352 {
353 #ifdef USE_FLASHROM
354 if (is_ap_write_protection_enabled(cfg)) {
355 ERROR("You must disable write protection before setting flags.\n");
356 return -1;
357 }
358 cfg->image.data = outbuf;
359 cfg->image.size = filesize;
360
361 const char *sections[] = {FMAP_RO_GBB};
362 int ret = write_system_firmware(cfg, &cfg->image, sections,
363 ARRAY_SIZE(sections));
364
365 cfg->image.data = NULL;
366 cfg->image.size = 0;
367 return ret;
368 #else
369 return 1;
370 #endif /* USE_FLASHROM */
371 }
372
parse_flag_value(const char * s,vb2_gbb_flags_t * val)373 static int parse_flag_value(const char *s, vb2_gbb_flags_t *val)
374 {
375 int sign = 0;
376
377 if (!strlen(s))
378 return -1;
379
380 if (s[0] == '+')
381 sign = 1;
382 else if (s[0] == '-')
383 sign = 2;
384
385 const char *ss = !sign ? s : &s[1];
386 char *e = NULL;
387 *val = strtoul(ss, &e, 0);
388 if (e && *e) {
389 ERROR("Invalid flags value: %s\n", ss);
390 return -1;
391 }
392
393 return sign;
394 }
395
do_gbb(int argc,char * argv[])396 static int do_gbb(int argc, char *argv[])
397 {
398 enum do_what_now { DO_GET, DO_SET, DO_CREATE } mode = DO_GET;
399 char *infile = NULL;
400 char *outfile = NULL;
401 char *opt_create = NULL;
402 char *opt_rootkey = NULL;
403 char *opt_bmpfv = NULL;
404 char *opt_recoverykey = NULL;
405 char *opt_hwid = NULL;
406 char *opt_flags = NULL;
407 bool sel_hwid = false;
408 bool sel_digest = false;
409 bool sel_flags = false;
410 int explicit_flags = 0;
411 uint8_t *inbuf = NULL;
412 off_t filesize;
413 uint8_t *outbuf = NULL;
414 int i;
415 struct updater_config *cfg = NULL;
416 struct updater_config_arguments args = {0};
417 int errorcnt = 0;
418
419
420 opterr = 0; /* quiet, you */
421 while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
422 #ifdef USE_FLASHROM
423 if (handle_flash_argument(&args, i, optarg))
424 continue;
425 #endif
426 switch (i) {
427 case 'g':
428 mode = DO_GET;
429 opt_has_arg("flags", 0);
430 opt_has_arg("hwid", 0);
431 break;
432 case 's':
433 mode = DO_SET;
434 opt_has_arg("flags", 1);
435 opt_has_arg("hwid", 1);
436 break;
437 case 'c':
438 mode = DO_CREATE;
439 opt_create = optarg;
440 break;
441 case 'o':
442 outfile = optarg;
443 break;
444 case 'k':
445 opt_rootkey = optarg;
446 break;
447 case 'b':
448 opt_bmpfv = optarg;
449 break;
450 case 'r':
451 opt_recoverykey = optarg;
452 break;
453 case OPT_HWID:
454 /* --hwid is optional: null might be okay */
455 opt_hwid = optarg;
456 sel_hwid = true;
457 break;
458 case OPT_FLAGS:
459 /* --flags is optional: null might be okay */
460 opt_flags = optarg;
461 sel_flags = true;
462 break;
463 case 'e':
464 sel_flags = true;
465 explicit_flags = 1;
466 break;
467 case OPT_DIGEST:
468 sel_digest = true;
469 break;
470 case OPT_FLASH:
471 #ifndef USE_FLASHROM
472 ERROR("futility was built without flashrom support\n");
473 return 1;
474 #endif
475 args.use_flash = 1;
476 break;
477 case OPT_HELP:
478 print_help(argc, argv);
479 return !!errorcnt;
480
481 case '?':
482 errorcnt++;
483 if (optopt)
484 ERROR("Unrecognized option: -%c\n", optopt);
485 else if (argv[optind - 1])
486 ERROR("Unrecognized option (possibly \"%s\")\n",
487 argv[optind - 1]);
488 else
489 ERROR("Unrecognized option\n");
490 break;
491 case ':':
492 errorcnt++;
493 if (argv[optind - 1])
494 ERROR("Missing argument to -%c (%s)\n", optopt,
495 argv[optind - 1]);
496 else
497 ERROR("Missing argument to -%c\n", optopt);
498 break;
499 default:
500 errorcnt++;
501 ERROR("While parsing options\n");
502 }
503 }
504
505 /* Problems? */
506 if (errorcnt) {
507 print_help(argc, argv);
508 return 1;
509 }
510
511 if (args.use_flash) {
512 if (setup_flash(&cfg, &args)) {
513 ERROR("While preparing flash\n");
514 return 1;
515 }
516 }
517
518 /* Now try to do something */
519 switch (mode) {
520 case DO_GET:
521 if (args.use_flash) {
522 inbuf = read_from_flash(cfg, &filesize);
523 } else {
524 if (argc - optind < 1) {
525 ERROR("Missing input filename\n");
526 print_help(argc, argv);
527 errorcnt++;
528 break;
529 }
530 infile = argv[optind++];
531 inbuf = read_entire_file(infile, &filesize);
532 }
533 if (!inbuf) {
534 errorcnt++;
535 break;
536 }
537
538 /* With no args, show the HWID */
539 if (!opt_rootkey && !opt_bmpfv && !opt_recoverykey
540 && !sel_flags && !sel_digest)
541 sel_hwid = true;
542
543 struct vb2_gbb_header *gbb = FindGbbHeader(inbuf, filesize);
544 if (!gbb) {
545 ERROR("No GBB found in %s\n", infile);
546 errorcnt++;
547 break;
548 }
549 uint8_t *gbb_base = (uint8_t *) gbb;
550
551 /* Get the stuff */
552 if (sel_hwid)
553 printf("hardware_id: %s\n",
554 gbb->hwid_size ? (char *)(gbb_base +
555 gbb->
556 hwid_offset) : "");
557 if (sel_digest)
558 print_hwid_digest(gbb, "digest: ");
559
560 if (sel_flags)
561 printf("flags: 0x%08x\n", gbb->flags);
562 if (opt_rootkey)
563 if (write_to_file(" - exported root_key to file:",
564 opt_rootkey,
565 gbb_base + gbb->rootkey_offset,
566 gbb->rootkey_size)) {
567 errorcnt++;
568 break;
569 }
570 if (opt_bmpfv)
571 if (write_to_file(
572 " - exported bmp_fv to file:", opt_bmpfv,
573 gbb_base + gbb->bmpfv_offset,
574 gbb->bmpfv_size)) {
575 errorcnt++;
576 break;
577 }
578 if (opt_recoverykey)
579 if (write_to_file(" - exported recovery_key to file:",
580 opt_recoverykey,
581 gbb_base + gbb->recovery_key_offset,
582 gbb->recovery_key_size)) {
583 errorcnt++;
584 break;
585 }
586 if (explicit_flags) {
587 vb2_gbb_flags_t remaining_flags = gbb->flags;
588 while (remaining_flags) {
589 vb2_gbb_flags_t lsb_flag =
590 remaining_flags & -remaining_flags;
591 remaining_flags &= ~lsb_flag;
592 const char *name;
593 const char *description;
594 if (vb2_get_gbb_flag_description(
595 lsb_flag, &name, &description) ==
596 VB2_SUCCESS) {
597 printf("%s\n", name);
598 } else {
599 printf("unknown set flag: 0x%08x\n",
600 lsb_flag);
601 }
602 }
603 }
604 break;
605
606 case DO_SET:
607 if (args.use_flash) {
608 inbuf = read_from_flash(cfg, &filesize);
609 } else {
610 if (argc - optind < 1) {
611 ERROR("Missing input filename\n");
612 print_help(argc, argv);
613 errorcnt++;
614 break;
615 }
616 infile = argv[optind++];
617 inbuf = read_entire_file(infile, &filesize);
618 if (!outfile)
619 outfile = (argc - optind < 1) ? infile
620 : argv[optind++];
621 }
622 if (!inbuf) {
623 errorcnt++;
624 break;
625 }
626
627 if (sel_hwid && !opt_hwid) {
628 ERROR("Missing new HWID value\n");
629 print_help(argc, argv);
630 errorcnt++;
631 break;
632 }
633 if (sel_flags && (!opt_flags || !*opt_flags)) {
634 ERROR("Missing new flags value\n");
635 print_help(argc, argv);
636 errorcnt++;
637 break;
638 }
639
640 gbb = FindGbbHeader(inbuf, filesize);
641 if (!gbb) {
642 ERROR("No GBB found in %s\n", infile);
643 errorcnt++;
644 break;
645 }
646 gbb_base = (uint8_t *) gbb;
647
648 outbuf = (uint8_t *) malloc(filesize);
649 if (!outbuf) {
650 ERROR("Can't malloc %" PRIi64 " bytes: %s\n", filesize,
651 strerror(errno));
652 errorcnt++;
653 break;
654 }
655
656 /* Switch pointers to outbuf */
657 memcpy(outbuf, inbuf, filesize);
658 gbb = FindGbbHeader(outbuf, filesize);
659 if (!gbb) {
660 ERROR("INTERNAL ERROR: No GBB found in outbuf\n");
661 errorcnt++;
662 break;
663 }
664 gbb_base = (uint8_t *) gbb;
665
666 if (opt_hwid) {
667 if (strlen(opt_hwid) + 1 > gbb->hwid_size) {
668 ERROR("null-terminated HWID exceeds capacity (%d)\n",
669 gbb->hwid_size);
670 errorcnt++;
671 break;
672 }
673 /* Wipe data before writing new value. */
674 memset(gbb_base + gbb->hwid_offset, 0,
675 gbb->hwid_size);
676 strcpy((char *)(gbb_base + gbb->hwid_offset),
677 opt_hwid);
678 update_hwid_digest(gbb);
679 }
680
681 if (opt_flags) {
682 vb2_gbb_flags_t val;
683 const int flag_sign = parse_flag_value(opt_flags, &val);
684 if (flag_sign < 0) {
685 errorcnt++;
686 break;
687 }
688 if (flag_sign > 0)
689 /* flag_sign := 1 => +ve and flag_sign := 2 => -ve. */
690 gbb->flags = flag_sign == 1 ? (gbb->flags | val) : (gbb->flags & ~val);
691 else
692 gbb->flags = val;
693 }
694
695 if (opt_rootkey) {
696 if (read_from_file("root_key", opt_rootkey,
697 gbb_base + gbb->rootkey_offset,
698 gbb->rootkey_size)) {
699 errorcnt++;
700 break;
701 }
702 }
703 if (opt_bmpfv)
704 if (read_from_file("bmp_fv", opt_bmpfv,
705 gbb_base + gbb->bmpfv_offset,
706 gbb->bmpfv_size)) {
707 errorcnt++;
708 break;
709 }
710 if (opt_recoverykey)
711 if (read_from_file("recovery_key", opt_recoverykey,
712 gbb_base + gbb->recovery_key_offset,
713 gbb->recovery_key_size)) {
714 errorcnt++;
715 break;
716 }
717
718 /* Write it out if there are no problems. */
719 if (!errorcnt) {
720 if (args.use_flash) {
721 if (write_to_flash(cfg, outbuf, filesize)) {
722 errorcnt++;
723 break;
724 }
725 } else if (write_to_file(
726 "successfully saved new image to:",
727 outfile, outbuf, filesize)) {
728 errorcnt++;
729 break;
730 }
731 }
732 break;
733
734 case DO_CREATE:
735 if (!outfile) {
736 if (argc - optind < 1) {
737 ERROR("Missing output filename\n");
738 print_help(argc, argv);
739 errorcnt++;
740 break;
741 }
742 outfile = argv[optind++];
743 }
744 /* Parse the creation args */
745 outbuf = create_gbb(opt_create, &filesize);
746 if (!outbuf) {
747 ERROR("Unable to parse creation spec (%s)\n", opt_create);
748 print_help(argc, argv);
749 errorcnt++;
750 break;
751 }
752 if (!errorcnt)
753 if (write_to_file("successfully created new GBB to:",
754 outfile, outbuf, filesize)) {
755 errorcnt++;
756 break;
757 }
758 break;
759 }
760
761 if (args.use_flash)
762 teardown_flash(cfg);
763 if (inbuf)
764 free(inbuf);
765 if (outbuf)
766 free(outbuf);
767 return !!errorcnt;
768 }
769
770 DECLARE_FUTIL_COMMAND(gbb, do_gbb, VBOOT_VERSION_ALL,
771 "Manipulate the Google Binary Block (GBB)");
772 DECLARE_FUTIL_COMMAND(gbb_utility, do_gbb, VBOOT_VERSION_ALL,
773 "Legacy name for `gbb` command");
774