Home | History | Annotate | Download | only in futility
      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