Extend HWID digest into PCR1 (GBB v1.2 only)

GBB header v1.2 adds a digest of the HWID string to the blob (and
maintains it when updated with the current futility/gbb_utility).

This CL causes VbSelectFirmware() to extend PCR1 with that HWID
digest (only for GBB header v1.2 and later, of course).

Tests are updated.

This also adds a "pcr" command to futility to help determine that
the change is working on the hardware (adds 4K bytes or fewer to
the size of the executable).

BUG=chromium:415714
BRANCH=ToT (and maybe others?)
TEST=make runtests, manual install on HW

To test on hardware, build and update a system with this change
(both the disk image and the RO firmware).

NOTE: The BIOS image must be built in a chroot that is using the
current version of futility. You may need to update your chroot
if your BIOS image still produces v1.1 GBB headers. Check with:

  futility show <firmware_image.bin> | grep -B1 'digest:'

Boot the new system with a new test image, then follow these steps:

Read the BIOS:

  # flashrom -r /tmp/bios.bin

Make sure the GBB has a valid digest for the HWID.

  # futility show /tmp/bios.bin | grep -B1 'digest:'
    HWID:                SAMUS TEST 8028
     digest:             4172d24f40bf72cc0ab8...  <valid>
  #

Extract only the sha1sum-sized part of the HWID digest:

  # futility show /tmp/bios.bin | awk '/digest:/ {print $2}' | colrm 41
  4172d24f40bf72cc0ab878b4c589b8fe9cf4405e
  #

Simulate extending that value in a PCR using the futility "pcr"
command:

  # futility pcr 4172d24f40bf72cc0ab878b4c589b8fe9cf4405e
  PCR: 0000000000000000000000000000000000000000
     + 4172d24f40bf72cc0ab878b4c589b8fe9cf4405e
  PCR: b6e5ffd2d898a7b15236ad22ca25f53ac1f40776
  #

Finally, look at the value of PCR1. It should match the last line
of the futility pcr output:

  # head /sys/class/misc/tpm0/device/pcrs | grep PCR-01
  PCR-01: B6 E5 FF D2 D8 98 A7 B1 52 36 AD 22 CA 25 F5 3A C1 F4 07 76
  #

Change-Id: I09cf855f1a24616cc1a9ddb676670edbc76827d2
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/226408
Reviewed-by: Darren Krahn <dkrahn@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
diff --git a/Makefile b/Makefile
index 8dee03c..c72c6a7 100644
--- a/Makefile
+++ b/Makefile
@@ -545,6 +545,7 @@
 	futility/cmd_dev_sign_file.c \
 	futility/cmd_dump_kernel_config.c \
 	futility/cmd_load_fmap.c \
+	futility/cmd_pcr.c \
 	futility/cmd_show.c \
 	futility/cmd_sign.c \
 	futility/cmd_vbutil_firmware.c \
diff --git a/firmware/lib/include/tpm_bootmode.h b/firmware/lib/include/tpm_bootmode.h
index 0a8044e..7550949 100644
--- a/firmware/lib/include/tpm_bootmode.h
+++ b/firmware/lib/include/tpm_bootmode.h
@@ -8,19 +8,22 @@
 #ifndef VBOOT_REFERENCE_TPM_BOOTMODE_H_
 #define VBOOT_REFERENCE_TPM_BOOTMODE_H_
 
+#include "gbb_header.h"
 #include "sysincludes.h"
 
 /**
  * Update TPM PCR State with the boot path status.
  *
  *  [developer_mode]: State of the developer switch.
- *  [recovery_mode}: State of the recovery mode.
+ *  [recovery_mode]: State of the recovery mode.
  *  [fw_keyblock_flags]: Keyblock flags of the to-be-booted
  *                       RW firmware keyblock.
+ *  [gbb]: Pointer to GBB header from RO firmware.
  *
  * Returns: TPM_SUCCESS if the TPM extend operation succeeds.
  */
 uint32_t SetTPMBootModeState(int developer_mode, int recovery_mode,
-			     uint64_t fw_keyblock_flags);
+			     uint64_t fw_keyblock_flags,
+			     GoogleBinaryBlockHeader *gbb);
 
 #endif  /* VBOOT_REFERENCE_TPM_BOOTMODE_H_ */
diff --git a/firmware/lib/mocked_tpm_bootmode.c b/firmware/lib/mocked_tpm_bootmode.c
index e9d82de..f8c0e8b 100644
--- a/firmware/lib/mocked_tpm_bootmode.c
+++ b/firmware/lib/mocked_tpm_bootmode.c
@@ -62,6 +62,7 @@
 
 
 uint32_t SetTPMBootModeState(int developer_mode, int recovery_mode,
-                             uint64_t fw_keyblock_flags) {
+			     uint64_t fw_keyblock_flags,
+			     GoogleBinaryBlockHeader *gbb)
   return TPM_SUCCESS;
 }
diff --git a/firmware/lib/tpm_bootmode.c b/firmware/lib/tpm_bootmode.c
index 0136bd9..cae4b0b 100644
--- a/firmware/lib/tpm_bootmode.c
+++ b/firmware/lib/tpm_bootmode.c
@@ -12,8 +12,9 @@
 #include "utility.h"
 #include "vboot_api.h"
 
-/* TPM PCR to use for storing boot mode measurements. */
+/* TPM PCRs to use for storing boot mode measurements. */
 #define BOOT_MODE_PCR 0
+#define HWID_DIGEST_PCR 1
 
 /*
  * Input digests for PCR extend.
@@ -134,9 +135,10 @@
 }
 
 uint32_t SetTPMBootModeState(int developer_mode, int recovery_mode,
-                             uint64_t fw_keyblock_flags)
+			     uint64_t fw_keyblock_flags,
+			     GoogleBinaryBlockHeader *gbb)
 {
-	uint32_t result;
+	uint32_t result0, result1 = 0;
 	const uint8_t *in_digest = NULL;
 	uint8_t out_digest[20];  /* For PCR extend output. */
 	int digest_index = GetBootStateIndex(developer_mode, recovery_mode,
@@ -150,9 +152,18 @@
 		in_digest = kBootInvalidSHA1Digest;
 	}
 
-	result = TlclExtend(BOOT_MODE_PCR, in_digest, out_digest);
-	VBDEBUG(("TPM: SetTPMBootModeState boot mode PCR out_digest "
-		 "%02x %02x %02x %02x\n",
-		 out_digest[0], out_digest[1], out_digest[2], out_digest[3]));
-	return result;
+	result0 = TlclExtend(BOOT_MODE_PCR, in_digest, out_digest);
+	VBDEBUG(("TPM: SetTPMBootModeState boot mode PCR%d result %d\n",
+		 BOOT_MODE_PCR, result0));
+
+	/* Extend the HWID Digest into PCR1 (GBB v1.2 and later only) */
+	if (gbb && gbb->minor_version >= 2) {
+		result1 = TlclExtend(HWID_DIGEST_PCR, gbb->hwid_digest,
+				     out_digest);
+		VBDEBUG(("TPM: SetTPMBootModeState HWID PCR%d result %d\n",
+			 HWID_DIGEST_PCR, result1));
+	}
+
+	/* The caller only looks for nonzero results, not error codes. */
+	return result0 || result1;
 }
diff --git a/firmware/lib/vboot_api_firmware.c b/firmware/lib/vboot_api_firmware.c
index cfe4251..c893d70 100644
--- a/firmware/lib/vboot_api_firmware.c
+++ b/firmware/lib/vboot_api_firmware.c
@@ -45,6 +45,15 @@
 		 */
 		VBDEBUG(("VbSelectFirmware() detected recovery request\n"));
 
+		/* Best effort to read the GBB */
+		cparams->gbb = VbExMalloc(sizeof(*cparams->gbb));
+		retval = VbGbbReadHeader_static(cparams, cparams->gbb);
+		if (VBERROR_SUCCESS != retval) {
+			VBDEBUG(("Can't read GBB. Continuing anyway...\n"));
+			VbExFree(cparams->gbb);
+			cparams->gbb = NULL;
+		}
+
 		/* Go directly to recovery mode */
 		fparams->selected_firmware = VB_SELECT_FIRMWARE_RECOVERY;
 	} else {
@@ -100,7 +109,8 @@
 	 * boot. Update the TPM with this state information.
 	 */
 	tpm_status = SetTPMBootModeState(is_dev, is_rec,
-					 shared->fw_keyblock_flags);
+					 shared->fw_keyblock_flags,
+					 cparams->gbb);
 	if (0 != tpm_status) {
 		VBDEBUG(("Can't update the TPM with boot mode information.\n"));
 		if (!is_rec) {
diff --git a/firmware/linktest/main.c b/firmware/linktest/main.c
index 2ff8e9d..b4ef4e5 100644
--- a/firmware/linktest/main.c
+++ b/firmware/linktest/main.c
@@ -18,83 +18,83 @@
 
 int main(void)
 {
-  /* cgptlib.h */
-  GptInit(0);
-  GptNextKernelEntry(0, 0, 0);
-  GptUpdateKernelEntry(0, 0);
+	/* cgptlib.h */
+	GptInit(0);
+	GptNextKernelEntry(0, 0, 0);
+	GptUpdateKernelEntry(0, 0);
 
-  /* load_firmware_fw.h */
-  LoadFirmware(0, 0, 0);
+	/* load_firmware_fw.h */
+	LoadFirmware(0, 0, 0);
 
-  /* load_kernel_fw.h */
-  LoadKernel(0, 0);
+	/* load_kernel_fw.h */
+	LoadKernel(0, 0);
 
-  /* rollback_index.h */
-  RollbackS3Resume();
-  RollbackFirmwareSetup(0, 0, 0, 0, 0);
-  RollbackFirmwareWrite(0);
-  RollbackFirmwareLock();
-  RollbackKernelRead(0);
-  RollbackKernelWrite(0);
-  RollbackKernelLock(0);
+	/* rollback_index.h */
+	RollbackS3Resume();
+	RollbackFirmwareSetup(0, 0, 0, 0, 0);
+	RollbackFirmwareWrite(0);
+	RollbackFirmwareLock();
+	RollbackKernelRead(0);
+	RollbackKernelWrite(0);
+	RollbackKernelLock(0);
 
-  /* tpm_bootmode.c */
-  SetTPMBootModeState(0, 0, 0);
+	/* tpm_bootmode.c */
+	SetTPMBootModeState(0, 0, 0, 0);
 
-  /* tlcl.h */
-  TlclStartup();
-  TlclResume();
-  TlclSelfTestFull();
-  TlclContinueSelfTest();
-  TlclDefineSpace(0, 0, 0);
-  TlclWrite(0, 0, 0);
-  TlclRead(0, 0, 0);
-  TlclWriteLock(0);
-  TlclReadLock(0);
-  TlclAssertPhysicalPresence();
-  TlclSetNvLocked();
-  TlclIsOwned();
-  TlclForceClear();
-  TlclSetEnable();
-  TlclClearEnable();
-  TlclSetDeactivated(0);
-  TlclGetFlags(0, 0, 0);
-  TlclSetGlobalLock();
-  TlclExtend(0, 0, 0);
-  TlclGetPermissions(0, 0);
+	/* tlcl.h */
+	TlclStartup();
+	TlclResume();
+	TlclSelfTestFull();
+	TlclContinueSelfTest();
+	TlclDefineSpace(0, 0, 0);
+	TlclWrite(0, 0, 0);
+	TlclRead(0, 0, 0);
+	TlclWriteLock(0);
+	TlclReadLock(0);
+	TlclAssertPhysicalPresence();
+	TlclSetNvLocked();
+	TlclIsOwned();
+	TlclForceClear();
+	TlclSetEnable();
+	TlclClearEnable();
+	TlclSetDeactivated(0);
+	TlclGetFlags(0, 0, 0);
+	TlclSetGlobalLock();
+	TlclExtend(0, 0, 0);
+	TlclGetPermissions(0, 0);
 
-  /* vboot_api.h - entry points INTO vboot_reference */
-  VbInit(0, 0);
-  VbSelectFirmware(0, 0);
-  VbUpdateFirmwareBodyHash(0, 0, 0);
-  VbSelectAndLoadKernel(0, 0);
+	/* vboot_api.h - entry points INTO vboot_reference */
+	VbInit(0, 0);
+	VbSelectFirmware(0, 0);
+	VbUpdateFirmwareBodyHash(0, 0, 0);
+	VbSelectAndLoadKernel(0, 0);
 
-  /* vboot_common.h */
-  OffsetOf(0, 0);
-  GetPublicKeyData(0);
-  GetPublicKeyDataC(0);
-  GetSignatureData(0);
-  GetSignatureDataC(0);
-  VerifyMemberInside(0, 0, 0, 0, 0, 0);
-  VerifyPublicKeyInside(0, 0, 0);
-  VerifySignatureInside(0, 0, 0);
-  PublicKeyInit(0, 0, 0);
-  PublicKeyCopy(0, 0);
-  PublicKeyToRSA(0);
-  VerifyData(0, 0, 0, 0);
-  VerifyDigest(0, 0, 0);
-  KeyBlockVerify(0, 0, 0, 0);
-  VerifyFirmwarePreamble(0, 0, 0);
-  VbGetFirmwarePreambleFlags(0);
-  VerifyKernelPreamble(0, 0, 0);
-  VbSharedDataInit(0, 0);
-  VbSharedDataReserve(0, 0);
-  VbSharedDataSetKernelKey(0, 0);
+	/* vboot_common.h */
+	OffsetOf(0, 0);
+	GetPublicKeyData(0);
+	GetPublicKeyDataC(0);
+	GetSignatureData(0);
+	GetSignatureDataC(0);
+	VerifyMemberInside(0, 0, 0, 0, 0, 0);
+	VerifyPublicKeyInside(0, 0, 0);
+	VerifySignatureInside(0, 0, 0);
+	PublicKeyInit(0, 0, 0);
+	PublicKeyCopy(0, 0);
+	PublicKeyToRSA(0);
+	VerifyData(0, 0, 0, 0);
+	VerifyDigest(0, 0, 0);
+	KeyBlockVerify(0, 0, 0, 0);
+	VerifyFirmwarePreamble(0, 0, 0);
+	VbGetFirmwarePreambleFlags(0);
+	VerifyKernelPreamble(0, 0, 0);
+	VbSharedDataInit(0, 0);
+	VbSharedDataReserve(0, 0);
+	VbSharedDataSetKernelKey(0, 0);
 
-  VbNvSetup(0);
-  VbNvGet(0, 0, 0);
-  VbNvSet(0, 0, 0);
-  VbNvTeardown(0);
+	VbNvSetup(0);
+	VbNvGet(0, 0, 0);
+	VbNvSet(0, 0, 0);
+	VbNvTeardown(0);
 
-  return 0;
+	return 0;
 }
diff --git a/futility/cmd_pcr.c b/futility/cmd_pcr.c
new file mode 100644
index 0000000..9149ccf
--- /dev/null
+++ b/futility/cmd_pcr.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <ctype.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "futility.h"
+
+static const char usage[] = "\n"
+	"Usage:  " MYNAME " %s [OPTIONS] DIGEST [...]\n"
+	"\n"
+	"This simulates a TPM PCR extension, to determine the expected output\n"
+	"\n"
+	"Each DIGEST arg should be a hex string (spaces optional) of the\n"
+	"appropriate length. The PCR is extended with each digest in turn\n"
+	"and the new value displayed.\n"
+	"\n"
+	"Options:\n"
+	"  -i      Initialize the PCR with the first DIGEST argument\n"
+	"            (the default is to start with all zeros)\n"
+	"  -2      Use sha256 DIGESTS (the default is sha1)\n"
+	"\n"
+	"Examples:\n"
+	"\n"
+	"  " MYNAME " %s b52791126f96a21a8ba4d511c6f25a1c1eb6dc9e\n"
+	"  " MYNAME " %s "
+	"'b5 27 91 12 6f 96 a2 1a 8b a4 d5 11 c6 f2 5a 1c 1e b6 dc 9e'\n"
+	"\n";
+
+static void help_and_quit(const char *prog)
+{
+	printf(usage, prog, prog, prog);
+}
+
+static int parse_hex(uint8_t *val, const char *str)
+{
+	uint8_t v = 0;
+	char c;
+	int digit;
+
+	for (digit = 0; digit < 2; digit++) {
+		c = *str;
+		if (!c)
+			return 0;
+		if (!isxdigit(c))
+			return 0;
+		c = tolower(c);
+		if (c >= '0' && c <= '9')
+			v += c - '0';
+		else
+			v += 10 + c - 'a';
+		if (!digit)
+			v <<= 4;
+		str++;
+	}
+
+	*val = v;
+	return 1;
+}
+
+static void parse_digest_or_die(uint8_t *buf, int len, const char *str)
+{
+	const char *s = str;
+	int i;
+
+	for (i = 0; i < len; i++) {
+		/* skip whitespace */
+		while (*s && isspace(*s))
+			s++;
+		if (!*s)
+			break;
+		if (!parse_hex(buf, s))
+			break;
+
+		/* on to the next byte */
+		s += 2;
+		buf++;
+	}
+
+	if (i != len) {
+		fprintf(stderr, "Invalid DIGEST \"%s\"\n", str);
+		exit(1);
+	}
+}
+
+static void print_digest(const uint8_t *buf, int len)
+{
+	int i;
+	for (i = 0; i < len; i++)
+		printf("%02x", buf[i]);
+}
+
+
+static int do_pcr(int argc, char *argv[])
+{
+	uint8_t accum[SHA256_DIGEST_SIZE * 2];
+	uint8_t pcr[SHA256_DIGEST_SIZE];
+	int digest_alg = SHA1_DIGEST_ALGORITHM;
+	int digest_size = SHA1_DIGEST_SIZE;
+	int opt_init = 0;
+	int errorcnt = 0;
+	uint8_t *digest;
+	int i;
+
+	opterr = 0;		/* quiet, you */
+	while ((i = getopt(argc, argv, ":i2")) != -1) {
+		switch (i) {
+		case 'i':
+			opt_init = 1;
+			break;
+		case '2':
+			digest_alg = SHA256_DIGEST_ALGORITHM;
+			digest_size = SHA256_DIGEST_SIZE;
+			break;
+		case '?':
+			if (optopt)
+				fprintf(stderr, "Unrecognized option: -%c\n",
+					optopt);
+			else
+				fprintf(stderr, "Unrecognized option\n");
+			errorcnt++;
+			break;
+		case ':':
+			fprintf(stderr, "Missing argument to -%c\n", optopt);
+			errorcnt++;
+			break;
+		default:
+			DIE;
+		}
+	}
+
+	if (errorcnt) {
+		help_and_quit(argv[0]);
+		return 1;
+	}
+
+	if (argc - optind < 1 + opt_init) {
+		fprintf(stderr, "You must extend at least one DIGEST\n");
+		help_and_quit(argv[0]);
+		return 1;
+	}
+
+	memset(pcr, 0, sizeof(pcr));
+
+	if (opt_init) {
+		parse_digest_or_die(pcr, digest_size, argv[optind]);
+		optind++;
+	}
+
+	printf("PCR: ");
+	print_digest(pcr, digest_size);
+	printf("\n");
+
+	for (i = optind; i < argc; i++) {
+		memcpy(accum, pcr, sizeof(pcr));
+		parse_digest_or_die(accum + digest_size, digest_size, argv[i]);
+
+		printf("   + ");
+		print_digest(accum + digest_size, digest_size);
+		printf("\n");
+
+		digest = DigestBuf(accum, digest_size * 2, digest_alg);
+		if (!digest) {
+			fprintf(stderr, "Error computing digest!\n");
+			return 1;
+		}
+		memcpy(pcr, digest, digest_size);
+		free(digest);
+
+		printf("PCR: ");
+		print_digest(pcr, digest_size);
+		printf("\n");
+	}
+
+	return 0;
+}
+
+DECLARE_FUTIL_COMMAND(pcr, do_pcr,
+		      "Simulate a TPM PCR extension operation",
+		      help_and_quit);
diff --git a/tests/tpm_bootmode_tests.c b/tests/tpm_bootmode_tests.c
index c39094d..64171b5 100644
--- a/tests/tpm_bootmode_tests.c
+++ b/tests/tpm_bootmode_tests.c
@@ -18,64 +18,135 @@
 
 extern const char* kBootStateSHA1Digests[];
 
-/* Last in_digest passed to TlclExtend() */
-static const uint8_t* last_in = NULL;
+/* Last in_digest passed to TlclExtend() for each PCR */
+static const uint8_t *last_in[20];
 
 /* Return value to pass for TlclExtend() */
-static uint32_t extend_returns = 0;
+static uint32_t extend_returns;
+
+/* How many calls to TlclExtend() should one SetTPMBootModeState() make? */
+static int expected_extend_count;
+/* How many did we get? */
+static int actual_extend_count;
+
+static GoogleBinaryBlockHeader gbb_v1 = {
+	.major_version = GBB_MAJOR_VER,
+	.minor_version = 1,
+};
+
+static GoogleBinaryBlockHeader gbb_v2 = {
+	.major_version = GBB_MAJOR_VER,
+	.minor_version = 2,
+	.hwid_digest = {1, 2, 3, 4,},
+};
 
 /* Mocked TlclExtend() function for testing */
-uint32_t TlclExtend(int pcr_num, const uint8_t* in_digest,
-                    uint8_t* out_digest) {
+uint32_t TlclExtend(int pcr_num, const uint8_t *in_digest,
+		    uint8_t *out_digest)
+{
+	/* Should be using correct pcr */
+	TEST_EQ(pcr_num, actual_extend_count, "TlclExtend pcr_num");
 
-  /* Should be using pcr 0 */
-  TEST_EQ(pcr_num, 0, "TlclExtend pcr_num");
+	last_in[actual_extend_count] = in_digest;
 
-  last_in = in_digest;
-
-  return extend_returns;
+	actual_extend_count++;
+	return extend_returns;
 }
 
 
 /* Test setting TPM boot mode state */
-static void BootStateTest(void) {
-  int recdev;
-  int flags;
-  int index;
-  char what[128];
+static void BootStateTest(void)
+{
+	int recdev;
+	int flags;
+	int index;
+	char what[128];
 
-  /* Test all permutations of developer and recovery mode */
-  for (recdev = 0; recdev < 4; recdev++) {
-    /* Exhaustively test all permutations of key block flags currently
-     * defined in vboot_struct.h (KEY_BLOCK_FLAG_*) */
-    for (flags = 0; flags < 16; flags++) {
-      index = recdev * 3;
-      if (6 == flags)
-        index += 2;
-      else if (7 == flags)
-        index += 1;
+	/* Test all permutations of developer and recovery mode */
+	for (recdev = 0; recdev < 4; recdev++) {
+		/* Exhaustively test all permutations of key block flags
+		 * currently defined in vboot_struct.h (KEY_BLOCK_FLAG_*) */
+		for (flags = 0; flags < 16; flags++) {
+			index = recdev * 3;
+			if (6 == flags)
+				index += 2;
+			else if (7 == flags)
+				index += 1;
 
-      last_in = NULL;
-      TEST_EQ(SetTPMBootModeState(recdev & 2, recdev & 1, flags), 0,
-              "SetTPMBootModeState return");
-      snprintf(what, sizeof(what), "SetTPMBootModeState %d, 0x%x",
-               recdev, flags);
-      TEST_PTR_EQ(last_in, kBootStateSHA1Digests[index], what);
-    }
-  }
+			/* Passing a null pointer for GBB */
+			memset(last_in, 0, sizeof(last_in));
+			actual_extend_count = 0;
+			expected_extend_count = 1;
+			TEST_EQ(SetTPMBootModeState(recdev & 2, recdev & 1,
+						    flags, 0), 0,
+				"SetTPMBootModeState return (gbb0)");
+			snprintf(what, sizeof(what),
+				 "SetTPMBootModeState %d, 0x%x (gbb0)",
+				 recdev, flags);
+			TEST_PTR_EQ(last_in[0],
+				    kBootStateSHA1Digests[index], what);
+			TEST_EQ(expected_extend_count, actual_extend_count,
+				"Expected TlclExtend call count (gbb0)");
+			snprintf(what, sizeof(what),
+				 "SetTPMBootModeState %d, 0x%x (gbb0) PCR1",
+				 recdev, flags);
+			TEST_PTR_EQ(last_in[1], NULL, what);
 
-  extend_returns = 1;
-  TEST_EQ(SetTPMBootModeState(0, 0, 0), 1, "SetTPMBootModeState error");
+			/* GBB v1.1 - should be exactly the same */
+			memset(last_in, 0, sizeof(last_in));
+			actual_extend_count = 0;
+			expected_extend_count = 1;
+			TEST_EQ(SetTPMBootModeState(recdev & 2, recdev & 1,
+						    flags, &gbb_v1), 0,
+				"SetTPMBootModeState return (gbb1)");
+			snprintf(what, sizeof(what),
+				 "SetTPMBootModeState %d, 0x%x (gbb1)",
+				 recdev, flags);
+			TEST_PTR_EQ(last_in[0],
+				    kBootStateSHA1Digests[index], what);
+			TEST_EQ(expected_extend_count, actual_extend_count,
+				"Expected TlclExtend call count (gbb1)");
+			snprintf(what, sizeof(what),
+				 "SetTPMBootModeState %d, 0x%x (gbb1) PCR1",
+				 recdev, flags);
+			TEST_PTR_EQ(last_in[1], NULL, what);
+
+			/* GBB v1.2 - should extend PCR1 with HWID digest */
+			memset(last_in, 0, sizeof(last_in));
+			actual_extend_count = 0;
+			expected_extend_count = 2;
+			TEST_EQ(SetTPMBootModeState(recdev & 2, recdev & 1,
+						    flags, &gbb_v2), 0,
+				"SetTPMBootModeState return (gbb2)");
+			snprintf(what, sizeof(what),
+				 "SetTPMBootModeState %d, 0x%x (gbb2)",
+				 recdev, flags);
+			TEST_PTR_EQ(last_in[0],
+				    kBootStateSHA1Digests[index], what);
+			TEST_EQ(expected_extend_count, actual_extend_count,
+				"Expected TlclExtend call count (gbb2)");
+			snprintf(what, sizeof(what),
+				 "SetTPMBootModeState %d, 0x%x (gbb2) PCR1",
+				 recdev, flags);
+			TEST_PTR_EQ(last_in[1], gbb_v2.hwid_digest, what);
+		}
+	}
+
+	extend_returns = 1;
+	actual_extend_count = 0;
+	expected_extend_count = 1;
+	TEST_EQ(SetTPMBootModeState(0, 0, 0, 0), 1,
+		"SetTPMBootModeState error");
 }
 
+int main(int argc, char *argv[])
+{
+	int error_code = 0;
 
-int main(int argc, char* argv[]) {
-  int error_code = 0;
+	BootStateTest();
 
-  BootStateTest();
+	if (!gTestSuccess)
+		error_code = 255;
 
-  if (!gTestSuccess)
-    error_code = 255;
-
-  return error_code;
+	return error_code;
 }
diff --git a/tests/vboot_api_firmware_tests.c b/tests/vboot_api_firmware_tests.c
index 9826e55..1b5c636 100644
--- a/tests/vboot_api_firmware_tests.c
+++ b/tests/vboot_api_firmware_tests.c
@@ -110,7 +110,8 @@
 }
 
 uint32_t SetTPMBootModeState(int developer_mode, int recovery_mode,
-                             uint64_t fw_keyblock_flags) {
+			     uint64_t fw_keyblock_flags,
+			     GoogleBinaryBlockHeader *gbb) {
   if (recovery_mode)
     mock_stbms_got_flags |= MOCK_REC_FLAG;
   if (developer_mode)
diff --git a/tests/vboot_region_tests.c b/tests/vboot_region_tests.c
index 1293136..80d01a2 100644
--- a/tests/vboot_region_tests.c
+++ b/tests/vboot_region_tests.c
@@ -116,7 +116,8 @@
 /* Mocked verification functions */
 
 uint32_t SetTPMBootModeState(int developer_mode, int recovery_mode,
-                             uint64_t fw_keyblock_flags) {
+			     uint64_t fw_keyblock_flags
+			     GoogleBinaryBlockHeader *gbb) {
   return VBERROR_SUCCESS;
 }