| Index: src/platform/vboot_reference/vboot_firmware/lib/rollback_index.c
|
| diff --git a/src/platform/vboot_reference/vboot_firmware/lib/rollback_index.c b/src/platform/vboot_reference/vboot_firmware/lib/rollback_index.c
|
| index 89e97a3ce977c30055edfe5331aaa29f54d37895..55d97d928365651d3be6567815179f6a99ad79a0 100644
|
| --- a/src/platform/vboot_reference/vboot_firmware/lib/rollback_index.c
|
| +++ b/src/platform/vboot_reference/vboot_firmware/lib/rollback_index.c
|
| @@ -19,50 +19,138 @@ uint16_t g_firmware_version = 0;
|
| uint16_t g_kernel_key_version = 0;
|
| uint16_t g_kernel_version = 0;
|
|
|
| -static void InitializeSpaces(void) {
|
| - uint16_t zero = 0;
|
| +static int InitializeSpaces(void) {
|
| + uint32_t zero = 0;
|
| + uint32_t space_holder;
|
| uint32_t firmware_perm = TPM_NV_PER_GLOBALLOCK | TPM_NV_PER_PPWRITE;
|
| uint32_t kernel_perm = TPM_NV_PER_PPWRITE;
|
|
|
| debug("Initializing spaces\n");
|
| - TlclSetNvLocked(); /* useful only the first time */
|
|
|
| - TlclDefineSpace(FIRMWARE_KEY_VERSION_NV_INDEX,
|
| - firmware_perm, sizeof(uint16_t));
|
| - TlclWrite(FIRMWARE_KEY_VERSION_NV_INDEX, (uint8_t*) &zero, sizeof(uint16_t));
|
| -
|
| - TlclDefineSpace(FIRMWARE_VERSION_NV_INDEX, firmware_perm, sizeof(uint16_t));
|
| - TlclWrite(FIRMWARE_VERSION_NV_INDEX, (uint8_t*) &zero, sizeof(uint16_t));
|
| -
|
| - TlclDefineSpace(KERNEL_KEY_VERSION_NV_INDEX, kernel_perm, sizeof(uint16_t));
|
| - TlclWrite(KERNEL_KEY_VERSION_NV_INDEX, (uint8_t*) &zero, sizeof(uint16_t));
|
| -
|
| - TlclDefineSpace(KERNEL_VERSION_NV_INDEX, kernel_perm, sizeof(uint16_t));
|
| - TlclWrite(KERNEL_VERSION_NV_INDEX, (uint8_t*) &zero, sizeof(uint16_t));
|
| + if (TlclRead(TPM_IS_INITIALIZED_NV_INDEX,
|
| + (uint8_t*) &space_holder, sizeof(space_holder)) == TPM_SUCCESS) {
|
| + /* Spaces are already initialized, so this is an error */
|
| + return 0;
|
| + }
|
| +
|
| + TlclSetNvLocked();
|
| +
|
| + TlclDefineSpace(FIRMWARE_VERSIONS_NV_INDEX, firmware_perm, sizeof(uint32_t));
|
| + TlclWrite(FIRMWARE_VERSIONS_NV_INDEX, (uint8_t*) &zero, sizeof(uint32_t));
|
| +
|
| + TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX, kernel_perm, sizeof(uint32_t));
|
| + TlclWrite(KERNEL_VERSIONS_NV_INDEX, (uint8_t*) &zero, sizeof(uint32_t));
|
| +
|
| + /* The space KERNEL_VERSIONS_BACKUP_NV_INDEX is used to protect the kernel
|
| + * versions when entering recovery mode. The content of space
|
| + * KERNEL_BACKUP_IS_VALID determines whether the backup value (1) or the
|
| + * regular value (0) should be trusted.
|
| + */
|
| + TlclDefineSpace(KERNEL_VERSIONS_BACKUP_NV_INDEX,
|
| + firmware_perm, sizeof(uint32_t));
|
| + TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX,
|
| + (uint8_t*) &zero, sizeof(uint32_t));
|
| + TlclDefineSpace(KERNEL_BACKUP_IS_VALID_NV_INDEX,
|
| + firmware_perm, sizeof(uint32_t));
|
| + TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX,
|
| + (uint8_t*) &zero, sizeof(uint32_t));
|
| +
|
| + /* The space TPM_IS_INITIALIZED_NV_INDEX is used to indicate that the TPM
|
| + * initialization has completed. Without it we cannot be sure that the last
|
| + * space to be created was also initialized (power could have been lost right
|
| + * after its creation).
|
| + */
|
| + TlclDefineSpace(TPM_IS_INITIALIZED_NV_INDEX, firmware_perm, sizeof(uint32_t));
|
| + return 1;
|
| }
|
|
|
| -static void EnterRecovery(void) {
|
| - /* Temporary recovery stub. Currently just initalizes spaces. */
|
| - InitializeSpaces();
|
| +/* Enters the recovery mode. If |unlocked| is true, there is some problem with
|
| + * the TPM, so do not attempt to do any more TPM operations, and particularly
|
| + * do not set bGlobalLock.
|
| + */
|
| +static void EnterRecovery(int unlocked) {
|
| + uint32_t combined_versions;
|
| + uint32_t one = 1;
|
| +
|
| + if (!unlocked) {
|
| + /* Saves the kernel versions and indicates that we should trust the saved
|
| + * ones.
|
| + */
|
| + TlclRead(KERNEL_VERSIONS_NV_INDEX, (uint8_t*) &combined_versions,
|
| + sizeof(uint32_t));
|
| + TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX, (uint8_t*) &combined_versions,
|
| + sizeof(uint32_t));
|
| + TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &one,
|
| + sizeof(uint32_t));
|
| + /* Protects the firmware and backup kernel versions. */
|
| + LockFirmwareVersions();
|
| + }
|
| + debug("entering recovery mode");
|
| +
|
| + /* and then what? */
|
| }
|
|
|
| static int GetTPMRollbackIndices(void) {
|
| - /* We just perform the reads, making sure they succeed. A failure means that
|
| - * the rollback index locations are some how messed up and we must jump to
|
| - * recovery */
|
| - if (TPM_SUCCESS != TlclRead(FIRMWARE_KEY_VERSION_NV_INDEX,
|
| - (uint8_t*) &g_firmware_key_version,
|
| - sizeof(g_firmware_key_version)) ||
|
| - TPM_SUCCESS != TlclRead(FIRMWARE_KEY_VERSION_NV_INDEX,
|
| - (uint8_t*) &g_firmware_key_version,
|
| - sizeof(g_firmware_key_version)) ||
|
| - TPM_SUCCESS != TlclRead(FIRMWARE_KEY_VERSION_NV_INDEX,
|
| - (uint8_t*) &g_firmware_key_version,
|
| - sizeof(g_firmware_key_version)) ||
|
| - TPM_SUCCESS != TlclRead(FIRMWARE_KEY_VERSION_NV_INDEX,
|
| - (uint8_t*) &g_firmware_key_version,
|
| - sizeof(g_firmware_key_version)))
|
| + uint32_t backup_is_valid;
|
| + uint32_t firmware_versions;
|
| + uint32_t kernel_versions;
|
| +
|
| + if (TlclRead(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &backup_is_valid,
|
| + sizeof(uint32_t)) != TPM_SUCCESS) {
|
| + EnterRecovery(1);
|
| + }
|
| + if (backup_is_valid) {
|
| + /* We reach this path if the previous boot went into recovery mode and we
|
| + * made a copy of the kernel versions to protect them.
|
| + */
|
| + uint32_t protected_combined_versions;
|
| + uint32_t unsafe_combined_versions;
|
| + uint32_t result;
|
| + uint32_t zero = 0;
|
| + if (TlclRead(KERNEL_VERSIONS_BACKUP_NV_INDEX,
|
| + (uint8_t*) &protected_combined_versions,
|
| + sizeof(uint32_t)) != TPM_SUCCESS) {
|
| + EnterRecovery(1);
|
| + }
|
| + result = TlclRead(KERNEL_VERSIONS_NV_INDEX,
|
| + (uint8_t*) &unsafe_combined_versions, sizeof(uint32_t));
|
| + if (result == TPM_E_BADINDEX) {
|
| + /* Jeez, someone removed the space. This is either hostile or extremely
|
| + * incompetent. Foo to them. Politeness and lack of an adequate
|
| + * character set prevent me from expressing my true feelings.
|
| + */
|
| + TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX, TPM_NV_PER_PPWRITE,
|
| + sizeof(uint32_t));
|
| + } else if (result != TPM_SUCCESS) {
|
| + EnterRecovery(1);
|
| + }
|
| + if (result == TPM_E_BADINDEX ||
|
| + protected_combined_versions != unsafe_combined_versions) {
|
| + TlclWrite(KERNEL_VERSIONS_NV_INDEX,
|
| + (uint8_t*) &protected_combined_versions, sizeof(uint32_t));
|
| + }
|
| + /* We recovered and now we can reset the BACKUP_IS_VALID flag.
|
| + */
|
| + TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &zero, 0);
|
| + }
|
| +
|
| + /* We perform the reads, making sure they succeed. A failure means that the
|
| + * rollback index locations are missing or somehow messed up. We let the
|
| + * caller deal with that.
|
| + */
|
| + if (TPM_SUCCESS != TlclRead(FIRMWARE_VERSIONS_NV_INDEX,
|
| + (uint8_t*) &firmware_versions,
|
| + sizeof(firmware_versions)) ||
|
| + TPM_SUCCESS != TlclRead(KERNEL_VERSIONS_NV_INDEX,
|
| + (uint8_t*) &kernel_versions,
|
| + sizeof(kernel_versions)))
|
| return 0;
|
| +
|
| + g_firmware_key_version = firmware_versions >> 16;
|
| + g_firmware_version = firmware_versions && 0xffff;
|
| + g_kernel_key_version = kernel_versions >> 16;
|
| + g_kernel_version = kernel_versions && 0xffff;
|
| +
|
| return 1;
|
| }
|
|
|
| @@ -84,61 +172,52 @@ void SetupTPM(void) {
|
| /* Check that the TPM is enabled and activated. */
|
| if(TlclGetFlags(&disable, &deactivated) != TPM_SUCCESS) {
|
| debug("failed to get TPM flags");
|
| - EnterRecovery();
|
| + EnterRecovery(1);
|
| }
|
| if (disable || deactivated) {
|
| TlclSetEnable();
|
| if (TlclSetDeactivated(0) != TPM_SUCCESS) {
|
| debug("failed to activate TPM");
|
| - EnterRecovery();
|
| + EnterRecovery(1);
|
| }
|
| }
|
| + /* We expect this to fail the first time we run on a device, indicating that
|
| + * the TPM has not been initialized yet. */
|
| if (!GetTPMRollbackIndices()) {
|
| debug("failed to get rollback indices");
|
| - EnterRecovery();
|
| + if (!InitializeSpaces()) {
|
| + /* If InitializeSpaces() fails (possibly because it had been executed
|
| + * already), something is wrong. */
|
| + EnterRecovery(1);
|
| + }
|
| }
|
| }
|
|
|
| -
|
| -uint16_t GetStoredVersion(int type) {
|
| +void GetStoredVersions(int type, uint16_t* key_version, uint16_t* version) {
|
| switch (type) {
|
| - case FIRMWARE_KEY_VERSION:
|
| - return g_firmware_key_version;
|
| + case FIRMWARE_VERSIONS:
|
| + *key_version = g_firmware_key_version;
|
| + *version = g_firmware_version;
|
| break;
|
| - case FIRMWARE_VERSION:
|
| - return g_firmware_version;
|
| - break;
|
| - case KERNEL_KEY_VERSION:
|
| - return g_kernel_key_version;
|
| - break;
|
| - case KERNEL_VERSION:
|
| - return g_kernel_version;
|
| + case KERNEL_VERSIONS:
|
| + *key_version = g_kernel_key_version;
|
| + *version = g_kernel_version;
|
| break;
|
| }
|
| - return 0;
|
| }
|
|
|
| -int WriteStoredVersion(int type, uint16_t version) {
|
| +int WriteStoredVersions(int type, uint16_t key_version, uint16_t version) {
|
| + uint32_t combined_version = (key_version << 16) & version;
|
| switch (type) {
|
| - case FIRMWARE_KEY_VERSION:
|
| - return (TPM_SUCCESS == TlclWrite(FIRMWARE_KEY_VERSION_NV_INDEX,
|
| - (uint8_t*) &version,
|
| - sizeof(uint16_t)));
|
| - break;
|
| - case FIRMWARE_VERSION:
|
| - return (TPM_SUCCESS == TlclWrite(FIRMWARE_VERSION_NV_INDEX,
|
| - (uint8_t*) &version,
|
| - sizeof(uint16_t)));
|
| - break;
|
| - case KERNEL_KEY_VERSION:
|
| - return (TPM_SUCCESS == TlclWrite(KERNEL_KEY_VERSION_NV_INDEX,
|
| - (uint8_t*) &version,
|
| - sizeof(uint16_t)));
|
| + case FIRMWARE_VERSIONS:
|
| + return (TPM_SUCCESS == TlclWrite(FIRMWARE_VERSIONS_NV_INDEX,
|
| + (uint8_t*) &combined_version,
|
| + sizeof(uint32_t)));
|
| break;
|
| - case KERNEL_VERSION:
|
| - return (TPM_SUCCESS == TlclWrite(KERNEL_VERSION_NV_INDEX,
|
| - (uint8_t*) &version,
|
| - sizeof(uint16_t)));
|
| + case KERNEL_VERSIONS:
|
| + return (TPM_SUCCESS == TlclWrite(KERNEL_VERSIONS_NV_INDEX,
|
| + (uint8_t*) &combined_version,
|
| + sizeof(uint32_t)));
|
| break;
|
| }
|
| return 0;
|
| @@ -147,13 +226,13 @@ int WriteStoredVersion(int type, uint16_t version) {
|
| void LockFirmwareVersions() {
|
| if (TlclSetGlobalLock() != TPM_SUCCESS) {
|
| debug("failed to set global lock");
|
| - EnterRecovery();
|
| + EnterRecovery(1);
|
| }
|
| }
|
|
|
| void LockKernelVersionsByLockingPP() {
|
| if (TlclLockPhysicalPresence() != TPM_SUCCESS) {
|
| debug("failed to turn off PP");
|
| - EnterRecovery();
|
| + EnterRecovery(1);
|
| }
|
| }
|
|
|