futility: Add global args to specify vboot API and format

The host-side futility tool will need to support all extant vboot
implementations. Some legacy futility commands only support the
original vb1 format, but others ("show" or "sign", for example)
may need to be instructed which formats to expect or emit.

This change adds some global args to specify the preferred
formats. It also cleans up a few [unused AFAICT] one-letter args
to avoid conflicts.

BUG=chromium:231574
BRANCH=none
TEST=make runtests

Nothing makes use of this yet, except the "help" command.

Change-Id: Ib79fa12af72b8860b9494e5d9e90b9572c006107
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/246765
Reviewed-by: Randall Spangler <rspangler@chromium.org>
diff --git a/futility/cmd_dump_fmap.c b/futility/cmd_dump_fmap.c
index 4bc4bd2..1c233fd 100644
--- a/futility/cmd_dump_fmap.c
+++ b/futility/cmd_dump_fmap.c
@@ -398,11 +398,11 @@
 	"Display (and extract) the FMAP components from a BIOS image.\n"
 	"\n"
 	"Options:\n"
-	"  -p             Use a format easy to parse by scripts\n"
-	"  -f             Use the format expected by flashrom\n"
+	"  -x             Extract the named sections from the file\n"
 	"  -h             Use a human-readable format\n"
 	"  -H             With -h, display any gaps\n"
-	"  -x             Extract the named sections from the file\n"
+	"  -p             Use a format easy to parse by scripts\n"
+	"  -F             Use the format expected by flashrom\n"
 	"\n"
 	"Specify one or more NAMEs to dump only those sections.\n"
 	"\n";
@@ -424,7 +424,7 @@
 	progname = argv[0];
 
 	opterr = 0;		/* quiet, you */
-	while ((c = getopt(argc, argv, ":xpfhH")) != -1) {
+	while ((c = getopt(argc, argv, ":xpFhH")) != -1) {
 		switch (c) {
 		case 'x':
 			opt_extract = 1;
@@ -432,7 +432,7 @@
 		case 'p':
 			opt_format = FMT_PRETTY;
 			break;
-		case 'f':
+		case 'F':
 			opt_format = FMT_FLASHROM;
 			break;
 		case 'H':
@@ -514,5 +514,6 @@
 }
 
 DECLARE_FUTIL_COMMAND(dump_fmap, do_dump_fmap,
+		      VBOOT_VERSION_ALL,
 		      "Display FMAP contents from a firmware image",
 		      print_help);
diff --git a/futility/cmd_dump_kernel_config.c b/futility/cmd_dump_kernel_config.c
index 520d280..fad7e29 100644
--- a/futility/cmd_dump_kernel_config.c
+++ b/futility/cmd_dump_kernel_config.c
@@ -88,5 +88,6 @@
 }
 
 DECLARE_FUTIL_COMMAND(dump_kernel_config, do_dump_kernel_config,
+		      VBOOT_VERSION_ALL,
 		      "Prints the kernel command line",
 		      PrintHelp);
diff --git a/futility/cmd_gbb_utility.c b/futility/cmd_gbb_utility.c
index 9e608d1..65d5f93 100644
--- a/futility/cmd_gbb_utility.c
+++ b/futility/cmd_gbb_utility.c
@@ -57,26 +57,39 @@
 }
 
 enum {
-	OPT_DIGEST = 1000,
+	OPT_HWID = 1000,
+	OPT_FLAGS,
+	OPT_DIGEST,
 };
 
 /* Command line options */
-static const struct option long_opts[] = {
-	/* name    hasarg *flag val */
+static struct option long_opts[] = {
+	/* name  has_arg *flag val */
 	{"get", 0, NULL, 'g'},
 	{"set", 0, NULL, 's'},
 	{"create", 1, NULL, 'c'},
 	{"output", 1, NULL, 'o'},
 	{"rootkey", 1, NULL, 'k'},
 	{"bmpfv", 1, NULL, 'b'},
-	{"recoverykey", 1, NULL, 'R'},
-	{"hwid", 2, NULL, 'i'},
-	{"flags", 2, NULL, 'L'},
+	{"recoverykey", 1, NULL, 'r'},
+	{"hwid", 0, NULL, OPT_HWID},
+	{"flags", 0, NULL, OPT_FLAGS},
 	{"digest", 0, NULL, OPT_DIGEST},
 	{NULL, 0, NULL, 0},
 };
 
-static char *short_opts = ":gsc:o:k:b:R:r:h:i:L:f:";
+static char *short_opts = ":gsc:o:k:b:r:";
+
+/* Change the has_arg field of a long_opts entry */
+static void opt_has_arg(const char *name, int val)
+{
+	struct option *p;
+	for (p = long_opts; p->name; p++)
+		if (!strcmp(name, p->name)) {
+			p->has_arg = val;
+			break;
+		}
+}
 
 static int errorcnt;
 
@@ -360,9 +373,13 @@
 		switch (i) {
 		case 'g':
 			mode = DO_GET;
+			opt_has_arg("flags", 0);
+			opt_has_arg("hwid", 0);
 			break;
 		case 's':
 			mode = DO_SET;
+			opt_has_arg("flags", 1);
+			opt_has_arg("hwid", 1);
 			break;
 		case 'c':
 			mode = DO_CREATE;
@@ -377,18 +394,15 @@
 		case 'b':
 			opt_bmpfv = optarg;
 			break;
-		case 'R':
 		case 'r':
 			opt_recoverykey = optarg;
 			break;
-		case 'i':
-		case 'h':
+		case OPT_HWID:
 			/* --hwid is optional: null might be okay */
 			opt_hwid = optarg;
 			sel_hwid = 1;
 			break;
-		case 'L':
-		case 'f':
+		case OPT_FLAGS:
 			/* --flags is optional: null might be okay */
 			opt_flags = optarg;
 			sel_flags = 1;
@@ -624,5 +638,6 @@
 }
 
 DECLARE_FUTIL_COMMAND(gbb_utility, do_gbb_utility,
+		      VBOOT_VERSION_ALL,
 		      "Manipulate the Google Binary Block (GBB)",
 		      print_help);
diff --git a/futility/cmd_load_fmap.c b/futility/cmd_load_fmap.c
index 7922ad6..cd81b30 100644
--- a/futility/cmd_load_fmap.c
+++ b/futility/cmd_load_fmap.c
@@ -199,5 +199,6 @@
 }
 
 DECLARE_FUTIL_COMMAND(load_fmap, do_load_fmap,
+		      VBOOT_VERSION_ALL,
 		      "Replace the contents of specified FMAP areas",
 		      help_and_quit);
diff --git a/futility/cmd_pcr.c b/futility/cmd_pcr.c
index 9149ccf..ef44453 100644
--- a/futility/cmd_pcr.c
+++ b/futility/cmd_pcr.c
@@ -182,5 +182,6 @@
 }
 
 DECLARE_FUTIL_COMMAND(pcr, do_pcr,
+		      VBOOT_VERSION_ALL,
 		      "Simulate a TPM PCR extension operation",
 		      help_and_quit);
diff --git a/futility/cmd_show.c b/futility/cmd_show.c
index b6c2574..875689d 100644
--- a/futility/cmd_show.c
+++ b/futility/cmd_show.c
@@ -730,6 +730,7 @@
 }
 
 DECLARE_FUTIL_COMMAND(show, do_show,
+		      VBOOT_VERSION_ALL,
 		      "Display the content of various binary components",
 		      print_help);
 
@@ -740,5 +741,6 @@
 }
 
 DECLARE_FUTIL_COMMAND(verify, do_verify,
+		      VBOOT_VERSION_ALL,
 		      "Verify the signatures of various binary components",
 		      print_help);
diff --git a/futility/cmd_sign.c b/futility/cmd_sign.c
index 2d247bc..192305f 100644
--- a/futility/cmd_sign.c
+++ b/futility/cmd_sign.c
@@ -1079,5 +1079,6 @@
 }
 
 DECLARE_FUTIL_COMMAND(sign, do_sign,
+		      VBOOT_VERSION_ALL,
 		      "Sign / resign various binary components",
 		      print_help);
diff --git a/futility/cmd_vbutil_firmware.c b/futility/cmd_vbutil_firmware.c
index dcb325e..4f748e8 100644
--- a/futility/cmd_vbutil_firmware.c
+++ b/futility/cmd_vbutil_firmware.c
@@ -386,5 +386,6 @@
 }
 
 DECLARE_FUTIL_COMMAND(vbutil_firmware, do_vbutil_firmware,
+		      VBOOT_VERSION_1_0,
 		      "Verified boot firmware utility",
 		      print_help);
diff --git a/futility/cmd_vbutil_kernel.c b/futility/cmd_vbutil_kernel.c
index 3322e4b..c7b3c78 100644
--- a/futility/cmd_vbutil_kernel.c
+++ b/futility/cmd_vbutil_kernel.c
@@ -648,5 +648,6 @@
 }
 
 DECLARE_FUTIL_COMMAND(vbutil_kernel, do_vbutil_kernel,
+		      VBOOT_VERSION_1_0,
 		      "Creates, signs, and verifies the kernel partition",
 		      print_help);
diff --git a/futility/cmd_vbutil_key.c b/futility/cmd_vbutil_key.c
index ea8dea0..de9f97f 100644
--- a/futility/cmd_vbutil_key.c
+++ b/futility/cmd_vbutil_key.c
@@ -235,5 +235,6 @@
 }
 
 DECLARE_FUTIL_COMMAND(vbutil_key, do_vbutil_key,
+		      VBOOT_VERSION_1_0,
 		      "Wraps RSA keys with vboot headers",
 		      print_help);
diff --git a/futility/cmd_vbutil_keyblock.c b/futility/cmd_vbutil_keyblock.c
index c73fd9a..64a1760 100644
--- a/futility/cmd_vbutil_keyblock.c
+++ b/futility/cmd_vbutil_keyblock.c
@@ -332,5 +332,6 @@
 }
 
 DECLARE_FUTIL_COMMAND(vbutil_keyblock, do_vbutil_keyblock,
+		      VBOOT_VERSION_1_0,
 		      "Creates, signs, and verifies a keyblock",
 		      print_help);
diff --git a/futility/futility.c b/futility/futility.c
index ba9fb16..02f61af 100644
--- a/futility/futility.c
+++ b/futility/futility.c
@@ -6,6 +6,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <getopt.h>
 #include <limits.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -175,8 +176,11 @@
 
 /******************************************************************************/
 
+/* Default is to support everything we can */
+enum vboot_version vboot_version = VBOOT_VERSION_ALL;
+
 static const char *const usage = "\n"
-"Usage: " MYNAME " COMMAND [args...]\n"
+"Usage: " MYNAME " [options] COMMAND [args...]\n"
 "\n"
 "This is the unified firmware utility, which will eventually replace\n"
 "most of the distinct verified boot tools formerly produced by the\n"
@@ -187,10 +191,12 @@
 "as " MYNAME ", followed by the original name as the first argument.\n"
 "\n";
 
-static void print_help(const char *cmd)
-{
-	puts(usage);
-}
+static const char *const options =
+"Global options:\n"
+"\n"
+"  --vb1        Use only vboot v1.0 binary formats\n"
+"  --vb21       Use only vboot v2.1 binary formats\n"
+"\n";
 
 static const struct futil_cmd_t *find_command(const char *name)
 {
@@ -208,12 +214,15 @@
 	const struct futil_cmd_t *const *cmd;
 
 	for (cmd = futil_cmds; *cmd; cmd++)
-		printf("  %-20s %s\n", (*cmd)->name, (*cmd)->shorthelp);
+		if (vboot_version & (*cmd)->version)
+			printf("  %-20s %s\n",
+			       (*cmd)->name, (*cmd)->shorthelp);
 }
 
 static int do_help(int argc, char *argv[])
 {
 	const struct futil_cmd_t *cmd;
+	const char *vstr;
 
 	if (argc >= 2) {
 		cmd = find_command(argv[1]);
@@ -227,16 +236,30 @@
 
 	fputs(usage, stdout);
 
-	printf("The following commands are built-in:\n\n");
+	if (vboot_version == VBOOT_VERSION_ALL)
+		fputs(options, stdout);
+
+	switch (vboot_version) {
+	case VBOOT_VERSION_1_0:
+		vstr = "version 1.0 ";
+		break;
+	case VBOOT_VERSION_2_1:
+		vstr = "version 2.1 ";
+		break;
+	case VBOOT_VERSION_ALL:
+		vstr = "";
+		break;
+	}
+	printf("The following %scommands are built-in:\n\n", vstr);
 	list_commands();
 	printf("\nUse \"" MYNAME " help COMMAND\" for more information.\n\n");
 
 	return 0;
 }
 
-DECLARE_FUTIL_COMMAND(help, do_help,
+DECLARE_FUTIL_COMMAND(help, do_help, VBOOT_VERSION_ALL,
 		      "Show a bit of help (you're looking at it)",
-		      print_help);
+		      NULL);
 
 static int do_version(int argc, char *argv[])
 {
@@ -244,7 +267,7 @@
 	return 0;
 }
 
-DECLARE_FUTIL_COMMAND(version, do_version,
+DECLARE_FUTIL_COMMAND(version, do_version, VBOOT_VERSION_ALL,
 		      "Show the futility source revision and build date",
 		      NULL);
 
@@ -276,6 +299,13 @@
 {
 	char *progname;
 	const struct futil_cmd_t *cmd;
+	int i, errorcnt = 0;
+	int vb_ver = VBOOT_VERSION_ALL;
+	struct option long_opts[] = {
+		{"vb1" , 0,  &vb_ver,  VBOOT_VERSION_1_0},
+		{"vb21", 0,  &vb_ver,  VBOOT_VERSION_2_1},
+		{ 0, 0, 0, 0},
+	};
 
 	log_args(argc, argv);
 
@@ -285,23 +315,48 @@
 	/* See if the program name is a command we recognize */
 	cmd = find_command(progname);
 	if (cmd)
+		/* Yep, just do that */
 		return run_command(cmd, argc, argv);
 
-	/* The program name means nothing, so we require an argument. */
-	if (argc < 2) {
+	/* Parse the global options, stopping at the first non-option. */
+	opterr = 0;				/* quiet, you. */
+	while ((i = getopt_long(argc, argv, "+:", long_opts, NULL)) != -1) {
+		switch (i) {
+		case '?':
+			if (optopt)
+				fprintf(stderr, "Unrecognized option: -%c\n",
+					optopt);
+			else
+				fprintf(stderr, "Unrecognized option: %s\n",
+					argv[optind - 1]);
+			errorcnt++;
+			break;
+		case ':':
+			fprintf(stderr, "Missing argument to -%c\n", optopt);
+			errorcnt++;
+			break;
+		case 0:				/* handled option */
+			break;
+		default:
+			Debug("i=%d\n", i);
+			DIE;
+		}
+	}
+	vboot_version = vb_ver;
+
+	/* Reset the getopt state so commands can parse their own options. */
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	/* We require a command name. */
+	if (errorcnt || argc < 1) {
 		do_help(0, 0);
 		return 1;
 	}
 
-	/* The first arg should be a command we recognize */
-	argc--;
-	argv++;
-
 	/* For reasons I've forgotten, treat /blah/blah/CMD the same as CMD */
 	progname = simple_basename(argv[0]);
-	/* Oh, and treat "--foo" the same as "foo" */
-	while (*progname == '-')
-		progname++;
 
 	/* Do we recognize the command? */
 	cmd = find_command(progname);
diff --git a/futility/futility.h b/futility/futility.h
index 5f9a5b7..550e381 100644
--- a/futility/futility.h
+++ b/futility/futility.h
@@ -17,21 +17,48 @@
 /* Version string (autogenerated) */
 extern const char futility_version[];
 
+/* Bitfields indicating the struct/format versions supported by a command */
+enum vboot_version  {
+	/*
+	 * v1.0 is the original structs used since the dawn of time.
+	 * v2.0 can verify the firmware in smaller chunks, but there's
+	 * no difference in the on-device structs, so it's only
+	 * meaningful for the firmware API. Futility doesn't care.
+	 */
+	VBOOT_VERSION_1_0 = 0x00000001,
+
+	/*
+	 * v2.1 uses new and different structs, and is what v2.0 would have
+	 * been if someone hadn't started using it before it was ready.
+	 */
+	VBOOT_VERSION_2_1 = 0x00000002,
+
+	/*
+	 * Everything we know about to date.
+	 */
+	VBOOT_VERSION_ALL = 0x00000003,
+};
+
+/* What's our preferred API & data format? */
+enum vboot_version vboot_version;
+
 /* Here's a structure to define the commands that futility implements. */
 struct futil_cmd_t {
 	const char *const name;
 	int (*const handler) (int argc, char **argv);
+	enum vboot_version version;
 	const char *const shorthelp;
 	void (*longhelp) (const char *cmd);
 };
 
 /* Macro to define a command */
-#define DECLARE_FUTIL_COMMAND(NAME, HANDLER, SHORTHELP, LONGHELP) \
-	const struct futil_cmd_t __cmd_##NAME = {                 \
-		.name = #NAME,                                    \
-		.handler = HANDLER,                               \
-		.shorthelp = SHORTHELP,				  \
-		.longhelp =  LONGHELP,				  \
+#define DECLARE_FUTIL_COMMAND(NAME, HANDLER, VERSION, SHORTHELP, LONGHELP) \
+	const struct futil_cmd_t __cmd_##NAME = {			\
+		.name = #NAME,						\
+		.handler = HANDLER,					\
+		.version = VERSION,					\
+		.shorthelp = SHORTHELP,					\
+		.longhelp =  LONGHELP,					\
 	}
 
 /* This is the list of pointers to all commands. */
diff --git a/tests/futility/test_dump_fmap.sh b/tests/futility/test_dump_fmap.sh
index bcdb27e..5a58a0c 100755
--- a/tests/futility/test_dump_fmap.sh
+++ b/tests/futility/test_dump_fmap.sh
@@ -10,7 +10,7 @@
 cd "$OUTDIR"
 
 # Good FMAP
-"$FUTILITY" dump_fmap -f "${SCRIPTDIR}/data_fmap.bin"  > "$TMP"
+"$FUTILITY" dump_fmap -F "${SCRIPTDIR}/data_fmap.bin"  > "$TMP"
 cmp "${SCRIPTDIR}/data_fmap_expect_f.txt" "$TMP"
 
 "$FUTILITY" dump_fmap -p "${SCRIPTDIR}/data_fmap.bin"  > "$TMP"
diff --git a/tests/futility/test_gbb_utility.sh b/tests/futility/test_gbb_utility.sh
index 3d8e576..3dc7d35 100755
--- a/tests/futility/test_gbb_utility.sh
+++ b/tests/futility/test_gbb_utility.sh
@@ -22,9 +22,11 @@
 ${FUTILITY} gbb_utility -g --flags ${TMP}.blob | grep -i 0xdeadbeef
 
 # HWID length should include the terminating null - this is too long
-if ${FUTILITY} gbb_utility -s -i "0123456789ABCDEF" ${TMP}.blob; then false; fi
+if ${FUTILITY} gbb_utility -s --hwid="0123456789ABCDEF" ${TMP}.blob; then
+  false;
+fi
 # This works
-${FUTILITY} gbb_utility -s -i "0123456789ABCDE" ${TMP}.blob
+${FUTILITY} gbb_utility -s --hwid="0123456789ABCDE" ${TMP}.blob
 # Read it back?
 ${FUTILITY} gbb_utility -g ${TMP}.blob | grep "0123456789ABCDE"
 
@@ -184,7 +186,7 @@
 # hwid_size == 0 doesn't complain, but can't be set
 cat ${TMP}.blob | ${REPLACE} 0x14 0x00 > ${TMP}.blob.bad
 ${FUTILITY} gbb_utility -g ${TMP}.blob.bad
-if ${FUTILITY} gbb_utility -s -i "A" ${TMP}.blob.bad; then false; fi
+if ${FUTILITY} gbb_utility -s --hwid="A" ${TMP}.blob.bad; then false; fi
 
 # rootkey_size == 0 gives warning, gets nothing, can't be set
 cat ${TMP}.blob | ${REPLACE} 0x1c 0x00 > ${TMP}.blob.bad
@@ -213,7 +215,7 @@
 
 # See that the digest is updated properly.
 hwid="123456789ABCDEF"
-${FUTILITY} gbb_utility -s -i ${hwid} ${TMP}.blob
+${FUTILITY} gbb_utility -s --hwid=${hwid} ${TMP}.blob
 expect=$(echo -n "$hwid" | sha256sum | cut -d ' ' -f 1)
 [ $(echo -n ${expect} | wc -c) == "64" ]
 ${FUTILITY} gbb_utility -g --digest ${TMP}.blob | grep ${expect}