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
21static 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
59enum {
60	OPT_HWID = 1000,
61	OPT_FLAGS,
62	OPT_DIGEST,
63};
64
65/* Command line options */
66static 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
81static char *short_opts = ":gsc:o:k:b:r:";
82
83/* Change the has_arg field of a long_opts entry */
84static 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
94static int errorcnt;
95
96#define GBB_SEARCH_STRIDE 4
97static 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
127static 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
199static 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
241fail:
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
253static 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
289static 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
335done_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
350static 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
640DECLARE_FUTIL_COMMAND(gbb_utility, do_gbb_utility,
641		      VBOOT_VERSION_ALL,
642		      "Manipulate the Google Binary Block (GBB)",
643		      print_help);
644