summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2016-04-15 14:49:40 -0700
committerRandall Spangler <rspangler@chromium.org>2016-07-29 17:30:55 +0000
commitb1d75add014b7383e00961156ce51749e6507465 (patch)
tree2d6692b7905ac6f6061a7385c2c9a8dcea49758f
parentb68a08bd257ad25c3be3ddb2c1c03b93aad68bb9 (diff)
downloadvboot-b1d75add014b7383e00961156ce51749e6507465.tar.gz
vboot: Add firmware management parameters
This adds RW firmware support for the optional firmware management parameters TPM space. System-level tests require CL:339262 to add cryptohome support. BUG=chromium:601492 BRANCH=baytrail and newer platforms TEST=make -j runtests Or better, COV=1 make, and then make sure all new code is covered. Additional manual tests. MUST use a test image for these, because a test image has a root shell even with dev mode disabled: Set FWMP: crossystem clear_tpm_owner_request=1 reboot cryptohome --action=tpm_take_ownership cryptohome --action=tpm_wait_ownership cryptohome --action=set_firmware_management_parameters --flags=1 cryptohome --action=get_firmware_management_parameters Reboot system with power+refresh+esc Use Ctrl+D then Enter to enable dev mode. Goes to the TONORM screen.   Pressing Esc doesn't exit it. Pressing Enter turns dev mode off. Then let it boot. Just to make sure FWMP did get set persistently: cryptohome --action=get_firmware_management_parameters Now remove the FWMP crossystem clear_tpm_owner_request=1 reboot cryptohome --action=tpm_take_ownership cryptohome --action=tpm_wait_ownership cryptohome --action=remove_firmware_management_parameters Reboot system with power+refresh+esc Use Ctrl+D then Enter to enable dev mode. Goes to the DEV screen. Change-Id: I0ac31bb2c64671ee9c3c810174baaf02b4cce641 Original-Change-Id: Ifaf644c80809552d5961615be6017c2a332a034b Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/339234 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/356790 Reviewed-by: Darren Krahn <dkrahn@chromium.org> Reviewed-by: Stefan Reinauer <reinauer@google.com>
-rw-r--r--firmware/include/gbb_header.h2
-rw-r--r--firmware/include/tss_constants.h2
-rw-r--r--firmware/include/vboot_api.h2
-rw-r--r--firmware/lib/include/load_kernel_fw.h4
-rw-r--r--firmware/lib/include/rollback_index.h40
-rw-r--r--firmware/lib/rollback_index.c71
-rw-r--r--firmware/lib/vboot_api_kernel.c71
-rw-r--r--firmware/lib/vboot_kernel.c38
-rw-r--r--tests/rollback_index2_tests.c123
-rw-r--r--tests/vboot_api_kernel2_tests.c43
-rw-r--r--tests/vboot_api_kernel4_tests.c12
-rw-r--r--tests/vboot_kernel_tests.c38
12 files changed, 442 insertions, 4 deletions
diff --git a/firmware/include/gbb_header.h b/firmware/include/gbb_header.h
index 86aa12a6..b600f120 100644
--- a/firmware/include/gbb_header.h
+++ b/firmware/include/gbb_header.h
@@ -62,6 +62,8 @@
#define GBB_FLAG_DISABLE_EC_SOFTWARE_SYNC 0x00000200
/* Default to booting legacy OS when dev screen times out */
#define GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY 0x00000400
+/* Disable using FWMP */
+#define GBB_FLAG_DISABLE_FWMP 0x00008000
#ifdef __cplusplus
extern "C" {
diff --git a/firmware/include/tss_constants.h b/firmware/include/tss_constants.h
index 883a5ad8..b2ddd7db 100644
--- a/firmware/include/tss_constants.h
+++ b/firmware/include/tss_constants.h
@@ -40,6 +40,8 @@
#define TPM_E_WRITE_FAILURE ((uint32_t)0x00005008) /* vboot local */
#define TPM_E_READ_EMPTY ((uint32_t)0x00005009) /* vboot local */
#define TPM_E_READ_FAILURE ((uint32_t)0x0000500a) /* vboot local */
+#define TPM_E_STRUCT_SIZE ((uint32_t) 0x0000500b) /* vboot local */
+#define TPM_E_STRUCT_VERSION ((uint32_t) 0x0000500c) /* vboot local */
#define TPM_NV_INDEX0 ((uint32_t)0x00000000)
#define TPM_NV_INDEX_LOCK ((uint32_t)0xffffffff)
diff --git a/firmware/include/vboot_api.h b/firmware/include/vboot_api.h
index 0ae6c76b..f3cc9138 100644
--- a/firmware/include/vboot_api.h
+++ b/firmware/include/vboot_api.h
@@ -112,6 +112,8 @@ enum VbErrorPredefined_t {
VBERROR_UNSUPPORTED_REGION = 0x10025,
/* No image present (returned from VbGbbReadImage() for missing image) */
VBERROR_NO_IMAGE_PRESENT = 0x10026,
+ /* Error reading FWMP from TPM (note: not present is not an error) */
+ VBERROR_TPM_READ_FWMP = 0x10029,
/* VbExEcGetExpectedRWHash() may return the following codes */
/* Compute expected RW hash from the EC image; BIOS doesn't have it */
diff --git a/firmware/lib/include/load_kernel_fw.h b/firmware/lib/include/load_kernel_fw.h
index a710ee5d..1e258a23 100644
--- a/firmware/lib/include/load_kernel_fw.h
+++ b/firmware/lib/include/load_kernel_fw.h
@@ -20,6 +20,8 @@
/* In recovery mode */
#define BOOT_FLAG_RECOVERY (0x02ULL)
+struct RollbackSpaceFwmp;
+
typedef struct LoadKernelParams {
/* Inputs to LoadKernel() */
/*
@@ -53,6 +55,8 @@ typedef struct LoadKernelParams {
* VbNvSetup() and VbNvTeardown() on the context.
*/
VbNvContext *nv_context;
+ /* Firmware management parameters; may be NULL if not present. */
+ const struct RollbackSpaceFwmp *fwmp;
/*
* Outputs from LoadKernel(); valid only if LoadKernel() returns
diff --git a/firmware/lib/include/rollback_index.h b/firmware/lib/include/rollback_index.h
index dd0de32a..6cc9ee61 100644
--- a/firmware/lib/include/rollback_index.h
+++ b/firmware/lib/include/rollback_index.h
@@ -18,7 +18,8 @@
/* This is just an opaque space for backup purposes */
#define BACKUP_NV_INDEX 0x1009
#define BACKUP_NV_SIZE 16
-
+#define FWMP_NV_INDEX 0x100a
+#define FWMP_NV_MAX_SIZE 128
/* Structure definitions for TPM spaces */
@@ -70,6 +71,34 @@ typedef struct RollbackSpaceFirmware {
uint8_t crc8;
} __attribute__((packed)) RollbackSpaceFirmware;
+#define FWMP_HASH_SIZE 32 /* Enough for SHA-256 */
+
+/* Firmware management parameters */
+struct RollbackSpaceFwmp {
+ /* CRC-8 of fields following struct_size */
+ uint8_t crc;
+ /* Structure size in bytes */
+ uint8_t struct_size;
+ /* Structure version */
+ uint8_t struct_version;
+ /* Reserved; ignored by current reader */
+ uint8_t reserved0;
+ /* Flags; see enum fwmp_flags */
+ uint32_t flags;
+ /* Hash of developer kernel key */
+ uint8_t dev_key_hash[FWMP_HASH_SIZE];
+} __attribute__((packed));
+
+#define ROLLBACK_SPACE_FWMP_VERSION 0x10 /* 1.0 */
+
+enum fwmp_flags {
+ FWMP_DEV_DISABLE_BOOT = (1 << 0),
+ FWMP_DEV_DISABLE_RECOVERY = (1 << 1),
+ FWMP_DEV_ENABLE_USB = (1 << 2),
+ FWMP_DEV_ENABLE_LEGACY = (1 << 3),
+ FWMP_DEV_ENABLE_OFFICIAL_ONLY = (1 << 4),
+ FWMP_DEV_USE_KEY_HASH = (1 << 5),
+};
/* All functions return TPM_SUCCESS (zero) if successful, non-zero if error */
@@ -134,6 +163,15 @@ uint32_t RollbackBackupWrite(uint8_t *raw);
*/
uint32_t RollbackKernelLock(int recovery_mode);
+/**
+ * Read and validate firmware management parameters.
+ *
+ * Absence of a FWMP is not an error; in this case, fwmp will be cleared.
+ *
+ * Returns non-zero if error.
+ */
+uint32_t RollbackFwmpRead(struct RollbackSpaceFwmp *fwmp);
+
/****************************************************************************/
/*
diff --git a/firmware/lib/rollback_index.c b/firmware/lib/rollback_index.c
index 306e9032..8aec1629 100644
--- a/firmware/lib/rollback_index.c
+++ b/firmware/lib/rollback_index.c
@@ -481,7 +481,6 @@ uint32_t SetupTPM(int developer_mode, int disable_dev_request,
return TPM_SUCCESS;
}
-
#ifdef DISABLE_ROLLBACK_TPM
/* Dummy implementations which don't support TPM rollback protection */
@@ -674,3 +673,73 @@ uint32_t RollbackKernelLock(int recovery_mode)
}
#endif /* DISABLE_ROLLBACK_TPM */
+
+uint32_t RollbackFwmpRead(struct RollbackSpaceFwmp *fwmp)
+{
+ uint8_t buf[FWMP_NV_MAX_SIZE];
+ struct RollbackSpaceFwmp *bf = (struct RollbackSpaceFwmp *)buf;
+ uint32_t r;
+ int attempts = 3;
+
+ /* Clear destination in case error or FWMP not present */
+ Memset(fwmp, 0, sizeof(*fwmp));
+
+ while (attempts--) {
+ /* Try to read entire 1.0 struct */
+ r = TlclRead(FWMP_NV_INDEX, buf, sizeof(*bf));
+ if (r == TPM_E_BADINDEX) {
+ /* Missing space is not an error; use defaults */
+ VBDEBUG(("TPM: %s() - no FWMP space\n", __func__));
+ return TPM_SUCCESS;
+ } else if (r != TPM_SUCCESS) {
+ VBDEBUG(("TPM: %s() - read returned 0x%x\n",
+ __func__, r));
+ return r;
+ }
+
+ /*
+ * Struct must be at least big enough for 1.0, but not bigger
+ * than our buffer size.
+ */
+ if (bf->struct_size < sizeof(*bf) ||
+ bf->struct_size > sizeof(buf))
+ return TPM_E_STRUCT_SIZE;
+
+ /*
+ * If space is bigger than we expect, re-read so we properly
+ * compute the CRC.
+ */
+ if (bf->struct_size > sizeof(*bf)) {
+ r = TlclRead(FWMP_NV_INDEX, buf, bf->struct_size);
+ if (r != TPM_SUCCESS)
+ return r;
+ }
+
+ /* Verify CRC */
+ if (bf->crc != Crc8(buf + 2, bf->struct_size - 2)) {
+ VBDEBUG(("TPM: %s() - bad CRC\n", __func__));
+ continue;
+ }
+
+ /* Verify major version is compatible */
+ if ((bf->struct_version >> 4) !=
+ (ROLLBACK_SPACE_FWMP_VERSION >> 4))
+ return TPM_E_STRUCT_VERSION;
+
+ /*
+ * Copy to destination. Note that if the space is bigger than
+ * we expect (due to a minor version change), we only copy the
+ * part of the FWMP that we know what to do with.
+ *
+ * If this were a 1.1+ reader and the source was a 1.0 struct,
+ * we would need to take care of initializing the extra fields
+ * added in 1.1+. But that's not an issue yet.
+ */
+ Memcpy(fwmp, bf, sizeof(*fwmp));
+ return TPM_SUCCESS;
+ }
+
+ VBDEBUG(("TPM: %s() - too many bad CRCs, giving up\n", __func__));
+ return TPM_E_CORRUPTED_STATE;
+}
+
diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c
index 18d00ae7..1a7cbe7d 100644
--- a/firmware/lib/vboot_api_kernel.c
+++ b/firmware/lib/vboot_api_kernel.c
@@ -22,14 +22,20 @@
/* Global variables */
static VbNvContext vnc;
+static struct RollbackSpaceFwmp fwmp;
#ifdef CHROMEOS_ENVIRONMENT
-/* Global variable accessor for unit tests */
+/* Global variable accessors for unit tests */
VbNvContext *VbApiKernelGetVnc(void)
{
return &vnc;
}
+
+struct RollbackSpaceFwmp *VbApiKernelGetFwmp(void)
+{
+ return &fwmp;
+}
#endif
/**
@@ -173,12 +179,18 @@ VbError_t VbBootNormal(VbCommonParams *cparams, LoadKernelParams *p)
return VbTryLoadKernel(cparams, p, VB_DISK_FLAG_FIXED);
}
+static const char dev_disable_msg[] =
+ "Developer mode is disabled on this device by system policy.\n"
+ "For more information, see http://dev.chromium.org/chromium-os/fwmp\n"
+ "\n";
+
VbError_t VbBootDeveloper(VbCommonParams *cparams, LoadKernelParams *p)
{
GoogleBinaryBlockHeader *gbb = cparams->gbb;
VbSharedDataHeader *shared =
(VbSharedDataHeader *)cparams->shared_data_blob;
uint32_t allow_usb = 0, allow_legacy = 0, ctrl_d_pressed = 0;
+ uint32_t disable_dev_boot = 0;
VbAudioContext *audio = 0;
VBDEBUG(("Entering %s()\n", __func__));
@@ -193,6 +205,46 @@ VbError_t VbBootDeveloper(VbCommonParams *cparams, LoadKernelParams *p)
if (gbb->flags & GBB_FLAG_FORCE_DEV_BOOT_LEGACY)
allow_legacy = 1;
+ /* Handle FWMP override */
+ if (fwmp.flags & FWMP_DEV_ENABLE_USB)
+ allow_usb = 1;
+ if (fwmp.flags & FWMP_DEV_ENABLE_LEGACY)
+ allow_legacy = 1;
+ if (fwmp.flags & FWMP_DEV_DISABLE_BOOT) {
+ if (gbb->flags & GBB_FLAG_FORCE_DEV_SWITCH_ON) {
+ VBDEBUG(("%s() - FWMP_DEV_DISABLE_BOOT rejected by "
+ "FORCE_DEV_SWITCH_ON\n",
+ __func__));
+ } else {
+ disable_dev_boot = 1;
+ }
+ }
+
+ /* If dev mode is disabled, only allow TONORM */
+ while (disable_dev_boot) {
+ VBDEBUG(("%s() - dev_disable_boot is set.\n", __func__));
+ VbDisplayScreen(cparams, VB_SCREEN_DEVELOPER_TO_NORM, 0, &vnc);
+ VbExDisplayDebugInfo(dev_disable_msg);
+
+ /* Ignore space in VbUserConfirms()... */
+ switch (VbUserConfirms(cparams, 0)) {
+ case 1:
+ VBDEBUG(("%s() - leaving dev-mode.\n", __func__));
+ VbNvSet(&vnc, VBNV_DISABLE_DEV_REQUEST, 1);
+ VbDisplayScreen(cparams,
+ VB_SCREEN_TO_NORM_CONFIRMED,
+ 0, &vnc);
+ VbExSleepMs(5000);
+ return VBERROR_TPM_REBOOT_REQUIRED;
+ case -1:
+ VBDEBUG(("%s() - shutdown requested\n", __func__));
+ return VBERROR_SHUTDOWN_REQUESTED;
+ default:
+ /* Ignore user attempt to cancel */
+ VBDEBUG(("%s() - ignore cancel TONORM\n", __func__));
+ }
+ }
+
/* Show the dev mode warning screen */
VbDisplayScreen(cparams, VB_SCREEN_DEVELOPER_WARNING, 0, &vnc);
@@ -907,12 +959,29 @@ VbError_t VbSelectAndLoadKernel(VbCommonParams *cparams,
}
shared->kernel_version_tpm_start = shared->kernel_version_tpm;
+ /* Read FWMP. Ignore errors in recovery mode. */
+ if (cparams->gbb->flags & GBB_FLAG_DISABLE_FWMP) {
+ Memset(&fwmp, 0, sizeof(fwmp));
+ tpm_status = 0;
+ } else {
+ tpm_status = RollbackFwmpRead(&fwmp);
+ }
+ if (0 != tpm_status) {
+ VBDEBUG(("Unable to get FWMP from TPM\n"));
+ if (!shared->recovery_reason) {
+ VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_R_ERROR);
+ retval = VBERROR_TPM_READ_FWMP;
+ goto VbSelectAndLoadKernel_exit;
+ }
+ }
+
/* Fill in params for calls to LoadKernel() */
Memset(&p, 0, sizeof(p));
p.shared_data_blob = cparams->shared_data_blob;
p.shared_data_size = cparams->shared_data_size;
p.gbb_data = cparams->gbb_data;
p.gbb_size = cparams->gbb_size;
+ p.fwmp = &fwmp;
/*
* This could be set to NULL, in which case the vboot header
diff --git a/firmware/lib/vboot_kernel.c b/firmware/lib/vboot_kernel.c
index 72cd2cc1..10e2ccb4 100644
--- a/firmware/lib/vboot_kernel.c
+++ b/firmware/lib/vboot_kernel.c
@@ -14,6 +14,7 @@
#include "gbb_access.h"
#include "gbb_header.h"
#include "load_kernel_fw.h"
+#include "rollback_index.h"
#include "utility.h"
#include "vboot_api.h"
#include "vboot_common.h"
@@ -206,6 +207,10 @@ VbError_t LoadKernel(LoadKernelParams *params, VbCommonParams *cparams)
} else if (dev_switch) {
boot_mode = kBootDev;
VbNvGet(vnc, VBNV_DEV_BOOT_SIGNED_ONLY, &require_official_os);
+
+ if (params->fwmp &&
+ (params->fwmp->flags & FWMP_DEV_ENABLE_OFFICIAL_ONLY))
+ require_official_os = 1;
} else {
boot_mode = kBootNormal;
}
@@ -395,6 +400,39 @@ VbError_t LoadKernel(LoadKernelParams *params, VbCommonParams *cparams)
goto bad_kernel;
}
+
+ /* If in developer mode and using key hash, check it */
+ if ((kBootDev == boot_mode) &&
+ params->fwmp &&
+ (params->fwmp->flags & FWMP_DEV_USE_KEY_HASH)) {
+ VbPublicKey *key = &key_block->data_key;
+ uint8_t *buf = ((uint8_t *)key) + key->key_offset;
+ uint64_t buflen = key->key_size;
+ uint8_t *digest;
+
+ VBDEBUG(("Checking developer key hash.\n"));
+ digest = DigestBuf(buf, buflen,
+ SHA256_DIGEST_ALGORITHM);
+ if (0 != SafeMemcmp(digest, params->fwmp->dev_key_hash,
+ SHA256_DIGEST_SIZE)) {
+ int i;
+
+ VBDEBUG(("Wrong developer key hash.\n"));
+ VBDEBUG(("Want: "));
+ for (i = 0; i < SHA256_DIGEST_SIZE; i++)
+ VBDEBUG(("%02x",
+ params->fwmp->dev_key_hash[i]));
+ VBDEBUG(("\nGot: "));
+ for (i = 0; i < SHA256_DIGEST_SIZE; i++)
+ VBDEBUG(("%02x", digest[i]));
+ VBDEBUG(("\n"));
+
+ VbExFree(digest);
+ goto bad_kernel;
+ }
+ VbExFree(digest);
+ }
+
/* Get key for preamble/data verification from the key block. */
data_key = PublicKeyToRSA(&key_block->data_key);
if (!data_key) {
diff --git a/tests/rollback_index2_tests.c b/tests/rollback_index2_tests.c
index 853bfc86..fb2f2e44 100644
--- a/tests/rollback_index2_tests.c
+++ b/tests/rollback_index2_tests.c
@@ -53,8 +53,21 @@ static int noise_on[MAX_NOISE_COUNT]; /* calls to inject noise on */
static TPM_PERMANENT_FLAGS mock_pflags;
static RollbackSpaceFirmware mock_rsf;
static RollbackSpaceKernel mock_rsk;
+
+static union {
+ struct RollbackSpaceFwmp fwmp;
+ uint8_t buf[FWMP_NV_MAX_SIZE];
+} mock_fwmp;
+
static uint32_t mock_permissions;
+/* Recalculate CRC of FWMP data */
+static void RecalcFwmpCrc(void)
+{
+ mock_fwmp.fwmp.crc = Crc8(mock_fwmp.buf + 2,
+ mock_fwmp.fwmp.struct_size - 2);
+}
+
/* Reset the variables for the Tlcl mock functions. */
static void ResetMocks(int fail_on_call, uint32_t fail_with_err)
{
@@ -70,6 +83,15 @@ static void ResetMocks(int fail_on_call, uint32_t fail_with_err)
Memset(&mock_rsf, 0, sizeof(mock_rsf));
Memset(&mock_rsk, 0, sizeof(mock_rsk));
mock_permissions = 0;
+
+ Memset(mock_fwmp.buf, 0, sizeof(mock_fwmp.buf));
+ mock_fwmp.fwmp.struct_size = sizeof(mock_fwmp.fwmp);
+ mock_fwmp.fwmp.struct_version = ROLLBACK_SPACE_FWMP_VERSION;
+ mock_fwmp.fwmp.flags = 0x1234;
+ /* Put some data in the hash */
+ mock_fwmp.fwmp.dev_key_hash[0] = 0xaa;
+ mock_fwmp.fwmp.dev_key_hash[FWMP_HASH_SIZE - 1] = 0xbb;
+ RecalcFwmpCrc();
}
/****************************************************************************/
@@ -135,6 +157,12 @@ uint32_t TlclRead(uint32_t index, void* data, uint32_t length)
TEST_EQ(length, sizeof(mock_rsk), "TlclRead rsk size");
Memcpy(data, &mock_rsk, length);
MaybeInjectNoise(data, length);
+ } else if (FWMP_NV_INDEX == index) {
+ Memset(data, 0, length);
+ if (length > sizeof(mock_fwmp))
+ length = sizeof(mock_fwmp);
+ Memcpy(data, &mock_fwmp, length);
+ MaybeInjectNoise(data, length);
} else {
Memset(data, 0, length);
}
@@ -962,6 +990,7 @@ static void RollbackKernelTest(void)
TEST_STR_EQ(mock_calls, "", "no tlcl calls");
}
+/****************************************************************************/
/* Tests for RollbackS3Resume() */
static void RollbackS3ResumeTest(void)
{
@@ -982,6 +1011,99 @@ static void RollbackS3ResumeTest(void)
"RollbackS3Resume() other error");
}
+/****************************************************************************/
+/* Tests for RollbackFwmpRead() calls */
+
+static void RollbackFwmpTest(void)
+{
+ struct RollbackSpaceFwmp fwmp;
+ struct RollbackSpaceFwmp fwmp_zero = {0};
+
+ /* Normal read */
+ ResetMocks(0, 0);
+ TEST_EQ(RollbackFwmpRead(&fwmp), 0, "RollbackFwmpRead()");
+ TEST_STR_EQ(mock_calls,
+ "TlclRead(0x100a, 40)\n",
+ " tlcl calls");
+ TEST_EQ(0, Memcmp(&fwmp, &mock_fwmp, sizeof(fwmp)), " data");
+
+ /* Read error */
+ ResetMocks(1, TPM_E_IOERROR);
+ TEST_EQ(RollbackFwmpRead(&fwmp), TPM_E_IOERROR,
+ "RollbackFwmpRead() error");
+ TEST_STR_EQ(mock_calls,
+ "TlclRead(0x100a, 40)\n",
+ " tlcl calls");
+ TEST_EQ(0, Memcmp(&fwmp, &fwmp_zero, sizeof(fwmp)), " data clear");
+
+ /* Not present isn't an error; just returns empty data */
+ ResetMocks(1, TPM_E_BADINDEX);
+ TEST_EQ(RollbackFwmpRead(&fwmp), 0, "RollbackFwmpRead() not present");
+ TEST_STR_EQ(mock_calls,
+ "TlclRead(0x100a, 40)\n",
+ " tlcl calls");
+ TEST_EQ(0, Memcmp(&fwmp, &fwmp_zero, sizeof(fwmp)), " data clear");
+
+ /* Struct size too small */
+ ResetMocks(0, 0);
+ mock_fwmp.fwmp.struct_size--;
+ TEST_EQ(RollbackFwmpRead(&fwmp), TPM_E_STRUCT_SIZE,
+ "RollbackFwmpRead() too small");
+
+ /* Struct size too large with good CRC */
+ ResetMocks(0, 0);
+ mock_fwmp.fwmp.struct_size += 4;
+ RecalcFwmpCrc();
+ TEST_EQ(RollbackFwmpRead(&fwmp), 0, "RollbackFwmpRead() bigger");
+ TEST_STR_EQ(mock_calls,
+ "TlclRead(0x100a, 40)\n"
+ "TlclRead(0x100a, 44)\n",
+ " tlcl calls");
+ TEST_EQ(0, Memcmp(&fwmp, &mock_fwmp, sizeof(fwmp)), " data");
+
+ /* Bad CRC causes retry, then eventual failure */
+ ResetMocks(0, 0);
+ mock_fwmp.fwmp.crc++;
+ TEST_EQ(RollbackFwmpRead(&fwmp), TPM_E_CORRUPTED_STATE,
+ "RollbackFwmpRead() crc");
+ TEST_STR_EQ(mock_calls,
+ "TlclRead(0x100a, 40)\n"
+ "TlclRead(0x100a, 40)\n"
+ "TlclRead(0x100a, 40)\n",
+ " tlcl calls");
+
+ /* Struct size too large with bad CRC */
+ ResetMocks(0, 0);
+ mock_fwmp.fwmp.struct_size += 4;
+ RecalcFwmpCrc();
+ mock_fwmp.fwmp.crc++;
+ TEST_EQ(RollbackFwmpRead(&fwmp), TPM_E_CORRUPTED_STATE,
+ "RollbackFwmpRead() bigger crc");
+ TEST_STR_EQ(mock_calls,
+ "TlclRead(0x100a, 40)\n"
+ "TlclRead(0x100a, 44)\n"
+ "TlclRead(0x100a, 40)\n"
+ "TlclRead(0x100a, 44)\n"
+ "TlclRead(0x100a, 40)\n"
+ "TlclRead(0x100a, 44)\n",
+ " tlcl calls");
+ TEST_EQ(0, Memcmp(&fwmp, &fwmp_zero, sizeof(fwmp)), " data");
+
+ /* Minor version difference ok */
+ ResetMocks(0, 0);
+ mock_fwmp.fwmp.struct_version++;
+ RecalcFwmpCrc();
+ TEST_EQ(RollbackFwmpRead(&fwmp), 0, "RollbackFwmpRead() minor version");
+ TEST_EQ(0, Memcmp(&fwmp, &mock_fwmp, sizeof(fwmp)), " data");
+
+ /* Major version difference not ok */
+ ResetMocks(0, 0);
+ mock_fwmp.fwmp.struct_version += 0x10;
+ RecalcFwmpCrc();
+ TEST_EQ(RollbackFwmpRead(&fwmp), TPM_E_STRUCT_VERSION,
+ "RollbackFwmpRead() major version");
+}
+
int main(int argc, char* argv[])
{
CrcTestFirmware();
@@ -992,6 +1114,7 @@ int main(int argc, char* argv[])
RollbackFirmwareTest();
RollbackKernelTest();
RollbackS3ResumeTest();
+ RollbackFwmpTest();
return gTestSuccess ? 0 : 255;
}
diff --git a/tests/vboot_api_kernel2_tests.c b/tests/vboot_api_kernel2_tests.c
index 21ea3063..44fdec50 100644
--- a/tests/vboot_api_kernel2_tests.c
+++ b/tests/vboot_api_kernel2_tests.c
@@ -42,6 +42,8 @@ static uint32_t screens_count = 0;
static uint32_t mock_num_disks[8];
static uint32_t mock_num_disks_count;
+extern struct RollbackSpaceFwmp *VbApiKernelGetFwmp(void);
+
/* Reset mock data (for use before each test) */
static void ResetMocks(void)
{
@@ -64,6 +66,8 @@ static void ResetMocks(void)
VbNvSetup(VbApiKernelGetVnc());
VbNvTeardown(VbApiKernelGetVnc()); /* So CRC gets generated */
+ Memset(VbApiKernelGetFwmp(), 0, sizeof(struct RollbackSpaceFwmp));
+
Memset(&shared_data, 0, sizeof(shared_data));
VbSharedDataInit(shared, sizeof(shared_data));
@@ -348,6 +352,12 @@ static void VbBootDevTest(void)
TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Ctrl+L nv legacy");
TEST_EQ(vbexlegacy_called, 1, " try legacy");
+ ResetMocks();
+ VbApiKernelGetFwmp()->flags |= FWMP_DEV_ENABLE_LEGACY;
+ mock_keypress[0] = 0x0c;
+ TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Ctrl+L fwmp legacy");
+ TEST_EQ(vbexlegacy_called, 1, " fwmp legacy");
+
/* Ctrl+U boots USB only if enabled */
ResetMocks();
mock_keypress[0] = 0x15;
@@ -367,6 +377,13 @@ static void VbBootDevTest(void)
vbtlk_retval = VBERROR_SUCCESS - VB_DISK_FLAG_REMOVABLE;
TEST_EQ(VbBootDeveloper(&cparams, &lkp), 0, "Ctrl+U force USB");
+ /* Ctrl+U enabled via FWMP */
+ ResetMocks();
+ VbApiKernelGetFwmp()->flags |= FWMP_DEV_ENABLE_USB;
+ mock_keypress[0] = 0x15;
+ vbtlk_retval = VBERROR_SUCCESS - VB_DISK_FLAG_REMOVABLE;
+ TEST_EQ(VbBootDeveloper(&cparams, &lkp), 0, "Ctrl+U force USB");
+
/* If no USB, eventually times out and tries fixed disk */
ResetMocks();
VbNvSet(VbApiKernelGetVnc(), VBNV_DEV_BOOT_USB, 1);
@@ -377,6 +394,32 @@ static void VbBootDevTest(void)
TEST_EQ(u, 0, " recovery reason");
TEST_EQ(audio_looping_calls_left, 0, " used up audio");
+ /* If dev mode is disabled, goes to TONORM screen repeatedly */
+ ResetMocks();
+ VbApiKernelGetFwmp()->flags |= FWMP_DEV_DISABLE_BOOT;
+ mock_keypress[0] = '\x1b'; /* Just causes TONORM again */
+ mock_keypress[1] = '\r';
+ TEST_EQ(VbBootDeveloper(&cparams, &lkp), VBERROR_TPM_REBOOT_REQUIRED,
+ "FWMP dev disabled");
+ TEST_EQ(screens_displayed[0], VB_SCREEN_DEVELOPER_TO_NORM,
+ " tonorm screen");
+ TEST_EQ(screens_displayed[1], VB_SCREEN_DEVELOPER_TO_NORM,
+ " tonorm screen");
+ TEST_EQ(screens_displayed[2], VB_SCREEN_TO_NORM_CONFIRMED,
+ " confirm screen");
+ VbNvGet(VbApiKernelGetVnc(), VBNV_DISABLE_DEV_REQUEST, &u);
+ TEST_EQ(u, 1, " disable dev request");
+
+ /* Shutdown requested when dev disabled */
+ ResetMocks();
+ shared->flags = VBSD_HONOR_VIRT_DEV_SWITCH | VBSD_BOOT_DEV_SWITCH_ON;
+ VbApiKernelGetFwmp()->flags |= FWMP_DEV_DISABLE_BOOT;
+ shutdown_request_calls_left = 1;
+ TEST_EQ(VbBootDeveloper(&cparams, &lkp), VBERROR_SHUTDOWN_REQUESTED,
+ "Shutdown requested when dev disabled");
+ TEST_EQ(screens_displayed[0], VB_SCREEN_DEVELOPER_TO_NORM,
+ " tonorm screen");
+
printf("...done.\n");
}
diff --git a/tests/vboot_api_kernel4_tests.c b/tests/vboot_api_kernel4_tests.c
index bb827024..27c4ef7c 100644
--- a/tests/vboot_api_kernel4_tests.c
+++ b/tests/vboot_api_kernel4_tests.c
@@ -31,7 +31,8 @@ static GoogleBinaryBlockHeader gbb;
static int ecsync_retval;
static uint32_t rkr_version;
static uint32_t new_version;
-static int rkr_retval, rkw_retval, rkl_retval;
+static struct RollbackSpaceFwmp rfr_fwmp;
+static int rkr_retval, rkw_retval, rkl_retval, rfr_retval;
static VbError_t vbboot_retval;
/* Reset mock data (for use before each test) */
@@ -57,6 +58,9 @@ static void ResetMocks(void)
Memset(&shared_data, 0, sizeof(shared_data));
VbSharedDataInit(shared, sizeof(shared_data));
+ Memset(&rfr_fwmp, 0, sizeof(rfr_fwmp));
+ rfr_retval = TPM_SUCCESS;
+
ecsync_retval = VBERROR_SUCCESS;
rkr_version = new_version = 0x10002;
rkr_retval = rkw_retval = rkl_retval = VBERROR_SUCCESS;
@@ -100,6 +104,12 @@ uint32_t RollbackKernelLock(int recovery_mode)
return rkl_retval;
}
+uint32_t RollbackFwmpRead(struct RollbackSpaceFwmp *fwmp)
+{
+ Memcpy(fwmp, &rfr_fwmp, sizeof(*fwmp));
+ return rfr_retval;
+}
+
VbError_t VbBootNormal(VbCommonParams *cparams, LoadKernelParams *p)
{
shared->kernel_version_tpm = new_version;
diff --git a/tests/vboot_kernel_tests.c b/tests/vboot_kernel_tests.c
index ee164cea..9f13f3cf 100644
--- a/tests/vboot_kernel_tests.c
+++ b/tests/vboot_kernel_tests.c
@@ -11,10 +11,12 @@
#include <string.h>
#include "cgptlib.h"
+#include "cryptolib.h"
#include "gbb_header.h"
#include "gpt.h"
#include "host_common.h"
#include "load_kernel_fw.h"
+#include "rollback_index.h"
#include "test_common.h"
#include "vboot_api.h"
#include "vboot_common.h"
@@ -57,6 +59,8 @@ static LoadKernelParams lkp;
static VbKeyBlockHeader kbh;
static VbKernelPreambleHeader kph;
static VbCommonParams cparams;
+static struct RollbackSpaceFwmp fwmp;
+static uint8_t mock_digest[SHA256_DIGEST_SIZE] = {12, 34, 56, 78};
static void ResetCallLog(void)
{
@@ -121,6 +125,9 @@ static void ResetMocks(void)
kph.bootloader_address = 0xbeadd008;
kph.bootloader_size = 0x1234;
+ memset(&fwmp, 0, sizeof(fwmp));
+ memcpy(fwmp.dev_key_hash, mock_digest, sizeof(fwmp.dev_key_hash));
+
memset(mock_parts, 0, sizeof(mock_parts));
mock_parts[0].start = 100;
mock_parts[0].size = 150; /* 75 KB */
@@ -229,6 +236,13 @@ int VerifyData(const uint8_t *data, uint64_t size, const VbSignature *sig,
return VBERROR_SUCCESS;
}
+uint8_t* DigestBuf(const uint8_t* buf, uint64_t len, int sig_algorithm)
+{
+ uint8_t *d = VbExMalloc(sizeof(mock_digest));
+
+ memcpy(d, mock_digest, sizeof(mock_digest));
+ return d;
+}
/**
* Test reading/writing GPT
@@ -442,6 +456,14 @@ static void LoadKernelTest(void)
TEST_EQ(LoadKernel(&lkp, &cparams), VBERROR_INVALID_KERNEL_FOUND,
"Fail key block dev sig");
+ ResetMocks();
+ lkp.boot_flags |= BOOT_FLAG_DEVELOPER;
+ lkp.fwmp = &fwmp;
+ fwmp.flags |= FWMP_DEV_ENABLE_OFFICIAL_ONLY;
+ key_block_verify_fail = 1;
+ TEST_EQ(LoadKernel(&lkp, &cparams), VBERROR_INVALID_KERNEL_FOUND,
+ "Fail key block dev sig fwmp");
+
/* Check key block flag mismatches */
ResetMocks();
kbh.key_block_flags =
@@ -527,6 +549,22 @@ static void LoadKernelTest(void)
lkp.boot_flags |= BOOT_FLAG_RECOVERY;
TEST_EQ(LoadKernel(&lkp, &cparams), 0, "Kernel version ignored in rec mode");
+ /* Check developer key hash - bad */
+ ResetMocks();
+ lkp.boot_flags |= BOOT_FLAG_DEVELOPER;
+ lkp.fwmp = &fwmp;
+ fwmp.flags |= FWMP_DEV_USE_KEY_HASH;
+ fwmp.dev_key_hash[0]++;
+ TEST_EQ(LoadKernel(&lkp, &cparams), VBERROR_INVALID_KERNEL_FOUND,
+ "Fail key block dev fwmp hash");
+
+ /* Check developer key hash - good */
+ ResetMocks();
+ lkp.boot_flags |= BOOT_FLAG_DEVELOPER;
+ lkp.fwmp = &fwmp;
+ fwmp.flags |= FWMP_DEV_USE_KEY_HASH;
+ TEST_EQ(LoadKernel(&lkp, &cparams), 0, "Good key block dev fwmp hash");
+
ResetMocks();
kph.preamble_size |= 0x07;
TEST_EQ(LoadKernel(&lkp, &cparams), VBERROR_INVALID_KERNEL_FOUND,