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 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 */ 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 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 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 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 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 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 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