summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--firmware/include/vboot_nvstorage.h24
-rw-r--r--firmware/lib/include/rollback_index.h15
-rw-r--r--firmware/lib/include/vboot_common.h7
-rw-r--r--firmware/lib/mocked_rollback_index.c15
-rw-r--r--firmware/lib/rollback_index.c42
-rw-r--r--firmware/lib/vboot_api_init.c37
-rw-r--r--firmware/lib/vboot_display.c2
-rw-r--r--firmware/lib/vboot_nvstorage.c92
-rw-r--r--host/lib/crossystem.c34
-rw-r--r--tests/rollback_index2_tests.c8
-rw-r--r--tests/vboot_api_init_tests.c246
-rw-r--r--utility/crossystem.c2
12 files changed, 511 insertions, 13 deletions
diff --git a/firmware/include/vboot_nvstorage.h b/firmware/include/vboot_nvstorage.h
index e6b014aa..534fb7e4 100644
--- a/firmware/include/vboot_nvstorage.h
+++ b/firmware/include/vboot_nvstorage.h
@@ -83,6 +83,8 @@ typedef enum VbNvParam {
VBNV_CLEAR_TPM_OWNER_DONE,
/* More details on recovery reason */
VBNV_RECOVERY_SUBCODE,
+ /* Request that NVRAM be backed up at next boot if possible. */
+ VBNV_BACKUP_NVRAM_REQUEST,
} VbNvParam;
/* Recovery reason codes for VBNV_RECOVERY_REQUEST */
@@ -260,4 +262,26 @@ int VbNvGet(VbNvContext *context, VbNvParam param, uint32_t *dest);
*/
int VbNvSet(VbNvContext *context, VbNvParam param, uint32_t value);
+/**
+ * Attempt to restore some fields of a lost VbNvContext from a backup area.
+ * The rest of the fields are unchanged, so they'd need to be set to their
+ * appropriate defaults by calling VbNvSetup() first (which is usually how we
+ * know the fields have been lost).
+ *
+ * Returns 0 if success, non-zero if error.
+ *
+ * This may only be called between VbNvSetup() and VbNvTeardown().
+ */
+int RestoreNvFromBackup(VbNvContext *vnc);
+
+/**
+ * Attempt to save some fields of the VbNvContext to a backup area.
+ *
+ * Returns 0 if success, non-zero if error. If it succeeds, it will clear the
+ * VBNV_BACKUP_NVRAM_REQUEST flag in the VbNvContext.
+ *
+ * This may only be called when the backup area is writable.
+ */
+int SaveNvToBackup(VbNvContext *vnc);
+
#endif /* VBOOT_REFERENCE_NVSTORAGE_H_ */
diff --git a/firmware/lib/include/rollback_index.h b/firmware/lib/include/rollback_index.h
index 7c410eec..dd0de32a 100644
--- a/firmware/lib/include/rollback_index.h
+++ b/firmware/lib/include/rollback_index.h
@@ -15,6 +15,10 @@
/* TPM NVRAM location indices. */
#define FIRMWARE_NV_INDEX 0x1007
#define KERNEL_NV_INDEX 0x1008
+/* This is just an opaque space for backup purposes */
+#define BACKUP_NV_INDEX 0x1009
+#define BACKUP_NV_SIZE 16
+
/* Structure definitions for TPM spaces */
@@ -66,6 +70,7 @@ typedef struct RollbackSpaceFirmware {
uint8_t crc8;
} __attribute__((packed)) RollbackSpaceFirmware;
+
/* All functions return TPM_SUCCESS (zero) if successful, non-zero if error */
/*
@@ -115,6 +120,16 @@ uint32_t RollbackKernelRead(uint32_t *version);
uint32_t RollbackKernelWrite(uint32_t version);
/**
+ * Read backup data.
+ */
+uint32_t RollbackBackupRead(uint8_t *raw);
+
+/**
+ * Write backup data.
+ */
+uint32_t RollbackBackupWrite(uint8_t *raw);
+
+/**
* Lock must be called. Internally, it's ignored in recovery mode.
*/
uint32_t RollbackKernelLock(int recovery_mode);
diff --git a/firmware/lib/include/vboot_common.h b/firmware/lib/include/vboot_common.h
index ca9abd0a..61c7431f 100644
--- a/firmware/lib/include/vboot_common.h
+++ b/firmware/lib/include/vboot_common.h
@@ -15,6 +15,13 @@
#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))
#endif
+/* Test an important condition at compile time, not run time */
+#define _BA1_(cond, line) \
+ extern int __build_assertion_ ## line[1 - 2*!(cond)] \
+ __attribute__ ((unused))
+#define _BA0_(c, x) _BA1_(c, x)
+#define BUILD_ASSERT(cond) _BA0_(cond, __LINE__)
+
/* Error Codes for all common functions. */
enum {
VBOOT_SUCCESS = 0,
diff --git a/firmware/lib/mocked_rollback_index.c b/firmware/lib/mocked_rollback_index.c
index d2f2bea5..86f223b2 100644
--- a/firmware/lib/mocked_rollback_index.c
+++ b/firmware/lib/mocked_rollback_index.c
@@ -7,6 +7,7 @@
*/
#include "sysincludes.h"
+#include "utility.h"
#include "rollback_index.h"
@@ -67,3 +68,17 @@ uint32_t RollbackKernelWrite(uint32_t version) {
uint32_t RollbackKernelLock(int recovery_mode) {
return TPM_SUCCESS;
}
+
+static uint8_t rollback_backup[BACKUP_NV_SIZE];
+
+uint32_t RollbackBackupRead(uint8_t *raw)
+{
+ Memcpy(raw, rollback_backup, BACKUP_NV_SIZE);
+ return TPM_SUCCESS;
+}
+
+uint32_t RollbackBackupWrite(uint8_t *raw)
+{
+ Memcpy(rollback_backup, raw, BACKUP_NV_SIZE);
+ return TPM_SUCCESS;
+}
diff --git a/firmware/lib/rollback_index.c b/firmware/lib/rollback_index.c
index e372d9b6..306e9032 100644
--- a/firmware/lib/rollback_index.c
+++ b/firmware/lib/rollback_index.c
@@ -308,15 +308,22 @@ uint32_t OneTimeInitializeTPM(RollbackSpaceFirmware *rsf,
Memcpy(rsf, &rsf_init, sizeof(RollbackSpaceFirmware));
Memcpy(rsk, &rsk_init, sizeof(RollbackSpaceKernel));
- /* Defines and sets firmware and kernel spaces */
+ /* Define the backup space. No need to initialize it, though. */
+ RETURN_ON_FAILURE(SafeDefineSpace(
+ BACKUP_NV_INDEX, TPM_NV_PER_PPWRITE, BACKUP_NV_SIZE));
+
+ /* Define and initialize the kernel space */
RETURN_ON_FAILURE(SafeDefineSpace(KERNEL_NV_INDEX, TPM_NV_PER_PPWRITE,
sizeof(RollbackSpaceKernel)));
RETURN_ON_FAILURE(WriteSpaceKernel(rsk));
+
+ /* Do the firmware space last, so we retry if we don't get this far. */
RETURN_ON_FAILURE(SafeDefineSpace(
FIRMWARE_NV_INDEX,
TPM_NV_PER_GLOBALLOCK | TPM_NV_PER_PPWRITE,
sizeof(RollbackSpaceFirmware)));
RETURN_ON_FAILURE(WriteSpaceFirmware(rsf));
+
return TPM_SUCCESS;
}
@@ -531,6 +538,16 @@ uint32_t RollbackKernelWrite(uint32_t version)
return TPM_SUCCESS;
}
+uint32_t RollbackBackupRead(uint8_t *raw)
+{
+ return TPM_SUCCESS;
+}
+
+uint32_t RollbackBackupWrite(uint8_t *raw)
+{
+ return TPM_SUCCESS;
+}
+
uint32_t RollbackKernelLock(int recovery_mode)
{
return TPM_SUCCESS;
@@ -625,6 +642,29 @@ uint32_t RollbackKernelWrite(uint32_t version)
return WriteSpaceKernel(&rsk);
}
+/*
+ * We don't really care whether the TPM owner has been messing with this or
+ * not. We lock it along with the Kernel space just to avoid problems, but it's
+ * only useful in dev-mode and only when the battery has been drained
+ * completely. There aren't any security issues. It's just in the TPM because
+ * we don't have any other place to keep it.
+ */
+uint32_t RollbackBackupRead(uint8_t *raw)
+{
+ uint32_t r;
+ r = TlclRead(BACKUP_NV_INDEX, raw, BACKUP_NV_SIZE);
+ VBDEBUG(("TPM: %s returning 0x%x\n", __func__, r));
+ return r;
+}
+
+uint32_t RollbackBackupWrite(uint8_t *raw)
+{
+ uint32_t r;
+ r = TlclWrite(BACKUP_NV_INDEX, raw, BACKUP_NV_SIZE);
+ VBDEBUG(("TPM: %s returning 0x%x\n", __func__, r));
+ return r;
+}
+
uint32_t RollbackKernelLock(int recovery_mode)
{
if (recovery_mode)
diff --git a/firmware/lib/vboot_api_init.c b/firmware/lib/vboot_api_init.c
index 58bc215f..b83214f2 100644
--- a/firmware/lib/vboot_api_init.c
+++ b/firmware/lib/vboot_api_init.c
@@ -36,6 +36,9 @@ VbError_t VbInit(VbCommonParams *cparams, VbInitParams *iparams)
uint32_t disable_dev_request = 0;
uint32_t clear_tpm_owner_request = 0;
int is_dev = 0;
+ uint32_t backup_requested = 0;
+ uint32_t backup_for_safety = 0;
+ int lost_nvram;
/* Initialize output flags */
iparams->out_flags = 0;
@@ -45,11 +48,12 @@ VbError_t VbInit(VbCommonParams *cparams, VbInitParams *iparams)
return retval;
VBDEBUG(("VbInit() input flags 0x%x gbb flags 0x%x\n", iparams->flags,
- gbb.flags));
+ gbb.flags));
/* Set up NV storage */
VbExNvStorageRead(vnc.raw);
VbNvSetup(&vnc);
+ lost_nvram = vnc.regenerate_crc;
/* Initialize shared data structure */
if (0 != VbSharedDataInit(shared, cparams->shared_data_size)) {
@@ -184,7 +188,7 @@ VbError_t VbInit(VbCommonParams *cparams, VbInitParams *iparams)
* dev-switch will be disabled by default)
*/
VBDEBUG(("TPM: Call RollbackFirmwareSetup(r%d, d%d)\n",
- recovery, is_hw_dev));
+ recovery, is_hw_dev));
tpm_status = RollbackFirmwareSetup(is_hw_dev,
disable_dev_request,
clear_tpm_owner_request,
@@ -248,6 +252,17 @@ VbError_t VbInit(VbCommonParams *cparams, VbInitParams *iparams)
}
}
+ /*
+ * If the nvram state was lost, try to restore the bits we care about
+ * from the backup in the TPM. It's okay if we can't, though.
+ * Note: None of the bits that we back up should have been referenced
+ * before this point. Otherwise, they'll just be overwritten here.
+ * All the other bits will be unchanged from whatever has happened to
+ * them since VbNvSetup() reinitialized the VbNvContext.
+ */
+ if (lost_nvram)
+ RestoreNvFromBackup(&vnc);
+
/* Allow BIOS to load arbitrary option ROMs? */
if (gbb.flags & GBB_FLAG_LOAD_OPTION_ROMS)
iparams->out_flags |= VB_INIT_OUT_ENABLE_OPROM;
@@ -295,6 +310,14 @@ VbError_t VbInit(VbCommonParams *cparams, VbInitParams *iparams)
VbNvSet(&vnc, VBNV_DEV_BOOT_USB, 0);
VbNvSet(&vnc, VBNV_DEV_BOOT_LEGACY, 0);
VbNvSet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, 0);
+ /*
+ * Back up any changes now, so these values can't be forgotten
+ * by draining the battery. We really only care about these
+ * three fields, but it's uncommon for any others to change so
+ * this is an easier test than checking each one.
+ */
+ if (vnc.regenerate_crc)
+ backup_for_safety = 1;
/*
* If we don't need the VGA option ROM but got it anyway, stop
@@ -309,7 +332,15 @@ VbError_t VbInit(VbCommonParams *cparams, VbInitParams *iparams)
}
}
- VbInit_exit:
+VbInit_exit:
+ /*
+ * If we successfully backup the NV storage, it will clear the
+ * VBNV_BACKUP_NVRAM_REQUEST field, so we want to do it before
+ * calling VbNvTeardown(). It's okay if we can't backup, though.
+ */
+ VbNvGet(&vnc, VBNV_BACKUP_NVRAM_REQUEST, &backup_requested);
+ if (backup_requested || backup_for_safety)
+ SaveNvToBackup(&vnc);
/* Tear down NV storage */
VbNvTeardown(&vnc);
diff --git a/firmware/lib/vboot_display.c b/firmware/lib/vboot_display.c
index 6e0c93ac..c66af8df 100644
--- a/firmware/lib/vboot_display.c
+++ b/firmware/lib/vboot_display.c
@@ -211,6 +211,7 @@ VbError_t VbDisplayScreenFromGBB(VbCommonParams *cparams, uint32_t screen,
if (localization >= hdr.number_of_localizations) {
localization = 0;
VbNvSet(vncptr, VBNV_LOCALIZATION_INDEX, localization);
+ VbNvSet(vncptr, VBNV_BACKUP_NVRAM_REQUEST, 1);
}
/* Display all bitmaps for the image */
@@ -641,6 +642,7 @@ VbError_t VbCheckDisplayKey(VbCommonParams *cparams, uint32_t key,
VBDEBUG(("VbCheckDisplayKey() - change localization to %d\n",
(int)loc));
VbNvSet(vncptr, VBNV_LOCALIZATION_INDEX, loc);
+ VbNvSet(vncptr, VBNV_BACKUP_NVRAM_REQUEST, 1);
#ifdef SAVE_LOCALE_IMMEDIATELY
VbNvTeardown(vncptr); /* really only computes checksum */
diff --git a/firmware/lib/vboot_nvstorage.c b/firmware/lib/vboot_nvstorage.c
index 9b2eca1b..258e5aff 100644
--- a/firmware/lib/vboot_nvstorage.c
+++ b/firmware/lib/vboot_nvstorage.c
@@ -10,6 +10,7 @@
#include "crc8.h"
#include "utility.h"
+#include "rollback_index.h"
#include "vboot_common.h"
#include "vboot_nvstorage.h"
@@ -27,6 +28,7 @@
#define BOOT_DEBUG_RESET_MODE 0x80
#define BOOT_DISABLE_DEV_REQUEST 0x40
#define BOOT_OPROM_NEEDED 0x20
+#define BOOT_BACKUP_NVRAM 0x10
#define BOOT_TRY_B_COUNT_MASK 0x0F
#define RECOVERY_OFFSET 2
@@ -153,6 +155,10 @@ int VbNvGet(VbNvContext *context, VbNvParam param, uint32_t *dest)
*dest = (raw[TPM_FLAGS_OFFSET] & TPM_CLEAR_OWNER_DONE ? 1 : 0);
return 0;
+ case VBNV_BACKUP_NVRAM_REQUEST:
+ *dest = (raw[BOOT_OFFSET] & BOOT_BACKUP_NVRAM ? 1 : 0);
+ return 0;
+
default:
return 1;
}
@@ -276,6 +282,14 @@ int VbNvSet(VbNvContext *context, VbNvParam param, uint32_t value)
raw[TPM_FLAGS_OFFSET] &= ~TPM_CLEAR_OWNER_DONE;
break;
+ case VBNV_BACKUP_NVRAM_REQUEST:
+ if (value)
+ raw[BOOT_OFFSET] |= BOOT_BACKUP_NVRAM;
+ else
+ raw[BOOT_OFFSET] &= ~BOOT_BACKUP_NVRAM;
+ break;
+
+
default:
return 1;
}
@@ -284,3 +298,81 @@ int VbNvSet(VbNvContext *context, VbNvParam param, uint32_t value)
context->regenerate_crc = 1;
return 0;
}
+
+/* These are the fields of the nvram that we want to back up. */
+static const VbNvParam backup_params[] = {
+ VBNV_KERNEL_FIELD,
+ VBNV_LOCALIZATION_INDEX,
+ VBNV_DEV_BOOT_USB,
+ VBNV_DEV_BOOT_LEGACY,
+ VBNV_DEV_BOOT_SIGNED_ONLY,
+};
+
+/* We can't back things up if there isn't enough storage. */
+BUILD_ASSERT(VBNV_BLOCK_SIZE <= BACKUP_NV_SIZE);
+
+int RestoreNvFromBackup(VbNvContext *vnc)
+{
+ VbNvContext bvnc;
+ uint32_t value;
+ int i;
+
+ VBDEBUG(("TPM: %s()\n", __func__));
+
+ if (TPM_SUCCESS != RollbackBackupRead(bvnc.raw))
+ return 1;
+
+ VbNvSetup(&bvnc);
+ if (bvnc.regenerate_crc) {
+ VBDEBUG(("TPM: Oops, backup is no good.\n"));
+ return 1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(backup_params); i++) {
+ VbNvGet(&bvnc, backup_params[i], &value);
+ VbNvSet(vnc, backup_params[i], value);
+ }
+
+ /* VbNvTeardown(&bvnc); is not needed. We're done with it. */
+ return 0;
+}
+
+int SaveNvToBackup(VbNvContext *vnc)
+{
+ VbNvContext bvnc;
+ uint32_t value;
+ int i;
+
+ VBDEBUG(("TPM: %s()\n", __func__));
+
+ /* Read it first. No point in writing the same data. */
+ if (TPM_SUCCESS != RollbackBackupRead(bvnc.raw))
+ return 1;
+
+ VbNvSetup(&bvnc);
+ VBDEBUG(("TPM: existing backup is %s\n",
+ bvnc.regenerate_crc ? "bad" : "good"));
+
+ for (i = 0; i < ARRAY_SIZE(backup_params); i++) {
+ VbNvGet(vnc, backup_params[i], &value);
+ VbNvSet(&bvnc, backup_params[i], value);
+ }
+
+ VbNvTeardown(&bvnc);
+
+ if (!bvnc.raw_changed) {
+ VBDEBUG(("TPM: Nothing's changed, not writing backup\n"));
+ /* Clear the request flag, since we're happy. */
+ VbNvSet(vnc, VBNV_BACKUP_NVRAM_REQUEST, 0);
+ return 0;
+ }
+
+ if (TPM_SUCCESS == RollbackBackupWrite(bvnc.raw)) {
+ /* Clear the request flag if we wrote successfully too */
+ VbNvSet(vnc, VBNV_BACKUP_NVRAM_REQUEST, 0);
+ return 0;
+ }
+
+ VBDEBUG(("TPM: Sorry, couldn't write backup.\n"));
+ return 1;
+}
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c
index ba614ccb..5e08c391 100644
--- a/host/lib/crossystem.c
+++ b/host/lib/crossystem.c
@@ -133,6 +133,19 @@ VbSetNvCleanup:
return retval;
}
+/*
+ * Set a param value, and try to flag it for persistent backup.
+ * It's okay if backup isn't supported. It's best-effort only.
+ */
+static int VbSetNvStorage_WithBackup(VbNvParam param, int value)
+{
+ int retval;
+ retval = VbSetNvStorage(param, value);
+ if (!retval)
+ VbSetNvStorage(VBNV_BACKUP_NVRAM_REQUEST, 1);
+ return retval;
+}
+
/* Find what build/debug status is specified on the kernel command
* line, if any. */
static VbBuildOption VbScanBuildOption(void) {
@@ -458,6 +471,8 @@ int VbGetSystemPropertyInt(const char* name) {
}
} else if (!strcasecmp(name,"loc_idx")) {
value = VbGetNvStorage(VBNV_LOCALIZATION_INDEX);
+ } else if (!strcasecmp(name,"backup_nvram_request")) {
+ value = VbGetNvStorage(VBNV_BACKUP_NVRAM_REQUEST);
} else if (!strcasecmp(name,"dev_boot_usb")) {
value = VbGetNvStorage(VBNV_DEV_BOOT_USB);
} else if (!strcasecmp(name,"dev_boot_legacy")) {
@@ -558,13 +573,18 @@ int VbSetSystemPropertyInt(const char* name, int value) {
return VbSetNvStorage(VBNV_CLEAR_TPM_OWNER_DONE, 0);
} else if (!strcasecmp(name,"fwb_tries")) {
return VbSetNvStorage(VBNV_TRY_B_COUNT, value);
+ } else if (!strcasecmp(name,"oprom_needed")) {
+ return VbSetNvStorage(VBNV_OPROM_NEEDED, value);
+ } else if (!strcasecmp(name,"backup_nvram_request")) {
+ /* Best-effort only, since it requires firmware and TPM support. */
+ return VbSetNvStorage(VBNV_BACKUP_NVRAM_REQUEST, value);
} else if (!strcasecmp(name,"fwupdate_tries")) {
int kern_nv = VbGetNvStorage(VBNV_KERNEL_FIELD);
if (kern_nv == -1)
return -1;
kern_nv &= ~KERN_NV_FWUPDATE_TRIES_MASK;
kern_nv |= (value & KERN_NV_FWUPDATE_TRIES_MASK);
- return VbSetNvStorage(VBNV_KERNEL_FIELD, kern_nv);
+ return VbSetNvStorage_WithBackup(VBNV_KERNEL_FIELD, kern_nv);
} else if (!strcasecmp(name,"block_devmode")) {
int kern_nv = VbGetNvStorage(VBNV_KERNEL_FIELD);
if (kern_nv == -1)
@@ -572,17 +592,15 @@ int VbSetSystemPropertyInt(const char* name, int value) {
kern_nv &= ~KERN_NV_BLOCK_DEVMODE_FLAG;
if (value)
kern_nv |= KERN_NV_BLOCK_DEVMODE_FLAG;
- return VbSetNvStorage(VBNV_KERNEL_FIELD, kern_nv);
+ return VbSetNvStorage_WithBackup(VBNV_KERNEL_FIELD, kern_nv);
} else if (!strcasecmp(name,"loc_idx")) {
- return VbSetNvStorage(VBNV_LOCALIZATION_INDEX, value);
+ return VbSetNvStorage_WithBackup(VBNV_LOCALIZATION_INDEX, value);
} else if (!strcasecmp(name,"dev_boot_usb")) {
- return VbSetNvStorage(VBNV_DEV_BOOT_USB, value);
+ return VbSetNvStorage_WithBackup(VBNV_DEV_BOOT_USB, value);
} else if (!strcasecmp(name,"dev_boot_legacy")) {
- return VbSetNvStorage(VBNV_DEV_BOOT_LEGACY, value);
+ return VbSetNvStorage_WithBackup(VBNV_DEV_BOOT_LEGACY, value);
} else if (!strcasecmp(name,"dev_boot_signed_only")) {
- return VbSetNvStorage(VBNV_DEV_BOOT_SIGNED_ONLY, value);
- } else if (!strcasecmp(name,"oprom_needed")) {
- return VbSetNvStorage(VBNV_OPROM_NEEDED, value);
+ return VbSetNvStorage_WithBackup(VBNV_DEV_BOOT_SIGNED_ONLY, value);
}
return -1;
diff --git a/tests/rollback_index2_tests.c b/tests/rollback_index2_tests.c
index 6d28a9c9..853bfc86 100644
--- a/tests/rollback_index2_tests.c
+++ b/tests/rollback_index2_tests.c
@@ -520,6 +520,8 @@ static void OneTimeInitTest(void)
"TlclForceClear()\n"
"TlclSetEnable()\n"
"TlclSetDeactivated(0)\n"
+ /* backup space */
+ "TlclDefineSpace(0x1009, 0x1, 16)\n"
/* kernel space */
"TlclDefineSpace(0x1008, 0x1, 13)\n"
"TlclWrite(0x1008, 13)\n"
@@ -549,6 +551,8 @@ static void OneTimeInitTest(void)
"TlclForceClear()\n"
"TlclSetEnable()\n"
"TlclSetDeactivated(0)\n"
+ /* backup space */
+ "TlclDefineSpace(0x1009, 0x1, 16)\n"
/* kernel space */
"TlclDefineSpace(0x1008, 0x1, 13)\n"
"TlclWrite(0x1008, 13)\n"
@@ -570,6 +574,8 @@ static void OneTimeInitTest(void)
"TlclForceClear()\n"
"TlclSetEnable()\n"
"TlclSetDeactivated(0)\n"
+ /* backup space */
+ "TlclDefineSpace(0x1009, 0x1, 16)\n"
/* kernel space */
"TlclDefineSpace(0x1008, 0x1, 13)\n"
"TlclWrite(0x1008, 13)\n"
@@ -664,6 +670,8 @@ static void SetupTpmTest(void)
"TlclForceClear()\n"
"TlclSetEnable()\n"
"TlclSetDeactivated(0)\n"
+ /* backup space */
+ "TlclDefineSpace(0x1009, 0x1, 16)\n"
"TlclDefineSpace(0x1008, 0x1, 13)\n"
"TlclWrite(0x1008, 13)\n"
"TlclRead(0x1008, 13)\n"
diff --git a/tests/vboot_api_init_tests.c b/tests/vboot_api_init_tests.c
index 9aa34b41..96ab9d33 100644
--- a/tests/vboot_api_init_tests.c
+++ b/tests/vboot_api_init_tests.c
@@ -32,6 +32,9 @@ static uint32_t mock_tpm_version;
static uint32_t mock_rfs_retval;
static int rfs_clear_tpm_request;
static int rfs_disable_dev_request;
+static uint8_t backup_space[BACKUP_NV_SIZE];
+static int backup_write_called;
+static int backup_read_called;
/* Reset mock data (for use before each test) */
static void ResetMocks(void)
@@ -53,6 +56,10 @@ static void ResetMocks(void)
VbNvSetup(&vnc);
VbNvTeardown(&vnc); /* So CRC gets generated */
+ Memset(backup_space, 0, sizeof(backup_space));
+ backup_write_called = 0;
+ backup_read_called = 0;
+
Memset(&shared_data, 0, sizeof(shared_data));
VbSharedDataInit(shared, sizeof(shared_data));
@@ -79,11 +86,25 @@ VbError_t VbExNvStorageRead(uint8_t *buf)
VbError_t VbExNvStorageWrite(const uint8_t *buf)
{
- nv_write_called = 1;
+ nv_write_called++;
Memcpy(vnc.raw, buf, sizeof(vnc.raw));
return VBERROR_SUCCESS;
}
+uint32_t RollbackBackupRead(uint8_t *raw)
+{
+ backup_read_called++;
+ Memcpy(raw, backup_space, sizeof(backup_space));
+ return TPM_SUCCESS;
+}
+
+uint32_t RollbackBackupWrite(uint8_t *raw)
+{
+ backup_write_called++;
+ Memcpy(backup_space, raw, sizeof(backup_space));
+ return TPM_SUCCESS;
+}
+
uint64_t VbExGetTimer(void)
{
/*
@@ -514,10 +535,233 @@ static void VbInitTestTPM(void)
TEST_EQ(rfs_clear_tpm_request, 1, "rfs tpm clear request");
}
+static void VbInitTestBackup(void)
+{
+ VbNvContext tmp_vnc;
+ uint32_t u, nv_w, bu_r;
+
+ ResetMocks();
+ /* Normal mode call */
+ TestVbInit(0, 0, "normal mode, no backup");
+ TEST_EQ(shared->flags, 0, " shared flags");
+ TEST_EQ(iparams.out_flags, 0, " out flags");
+ TEST_EQ(nv_write_called, 0,
+ " NV write not called since nothing changed");
+
+ ResetMocks();
+ /* Now set some params that should be backed up. */
+ VbNvSet(&vnc, VBNV_KERNEL_FIELD, 0xaabbccdd);
+ VbNvSet(&vnc, VBNV_LOCALIZATION_INDEX, 0xa5);
+ VbNvSet(&vnc, VBNV_DEV_BOOT_USB, 1);
+ VbNvSet(&vnc, VBNV_DEV_BOOT_LEGACY, 1);
+ VbNvSet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, 1);
+ /* and some that don't */
+ VbNvSet(&vnc, VBNV_OPROM_NEEDED, 1);
+ VbNvSet(&vnc, VBNV_TRY_B_COUNT, 3);
+ /* Make sure they're clean */
+ VbNvTeardown(&vnc);
+ /* Normal mode call */
+ TestVbInit(0, 0, "normal mode, some backup");
+ TEST_EQ(shared->flags, 0, " shared flags");
+ TEST_EQ(iparams.out_flags, 0, " out flags");
+ TEST_EQ(nv_write_called, 1,
+ " Write NV because things have changed");
+ /* Some fields should be unchanged */
+ VbNvGet(&vnc, VBNV_KERNEL_FIELD, &u);
+ TEST_EQ(u, 0xaabbccdd, " NV kernel field");
+ VbNvGet(&vnc, VBNV_LOCALIZATION_INDEX, &u);
+ TEST_EQ(u, 0xa5, " NV localization index");
+ VbNvGet(&vnc, VBNV_OPROM_NEEDED, &u);
+ TEST_EQ(u, 1, " NV oprom_needed");
+ VbNvGet(&vnc, VBNV_TRY_B_COUNT, &u);
+ TEST_EQ(u, 3, " NV try_b_count");
+ /* But normal mode should have cleared the DEV_BOOT flags */
+ VbNvGet(&vnc, VBNV_DEV_BOOT_USB, &u);
+ TEST_EQ(u, 0, " NV dev_boot_usb");
+ VbNvGet(&vnc, VBNV_DEV_BOOT_LEGACY, &u);
+ TEST_EQ(u, 0, " NV dev_boot_legacy");
+ VbNvGet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, &u);
+ TEST_EQ(u, 0, " NV dev_boot_signed_only");
+ /* So we should have written the backup */
+ TEST_EQ(backup_write_called, 1, " Backup written once");
+ /* And the backup should reflect the persisent flags. */
+ Memset(&tmp_vnc, 0, sizeof(tmp_vnc));
+ TEST_EQ(0, RestoreNvFromBackup(&tmp_vnc), "read from backup");
+ VbNvGet(&tmp_vnc, VBNV_KERNEL_FIELD, &u);
+ TEST_EQ(u, 0xaabbccdd, " BU kernel field");
+ VbNvGet(&tmp_vnc, VBNV_LOCALIZATION_INDEX, &u);
+ TEST_EQ(u, 0xa5, " BU localization index");
+ VbNvGet(&tmp_vnc, VBNV_DEV_BOOT_USB, &u);
+ TEST_EQ(u, 0, " BU dev_boot_usb");
+ VbNvGet(&tmp_vnc, VBNV_DEV_BOOT_LEGACY, &u);
+ TEST_EQ(u, 0, " BU dev_boot_legacy");
+ VbNvGet(&tmp_vnc, VBNV_DEV_BOOT_SIGNED_ONLY, &u);
+ TEST_EQ(u, 0, " BU dev_boot_signed_only");
+ /* but not the others */
+ VbNvGet(&tmp_vnc, VBNV_OPROM_NEEDED, &u);
+ TEST_EQ(u, 0, " BU oprom_needed");
+ VbNvGet(&tmp_vnc, VBNV_TRY_B_COUNT, &u);
+ TEST_EQ(u, 0, " BU try_b_count");
+
+ /*
+ * If we change one of the non-backed-up NVRAM params and try
+ * again, we shouldn't need to backup again.
+ */
+ VbNvSet(&vnc, VBNV_OPROM_NEEDED, 0);
+ VbNvSet(&vnc, VBNV_TRY_B_COUNT, 2);
+ /* Make sure they're clean */
+ VbNvTeardown(&vnc);
+ /* Normal mode call */
+ TestVbInit(0, 0, "normal mode, expect no backup");
+ TEST_EQ(shared->flags, 0, " shared flags");
+ TEST_EQ(iparams.out_flags, 0, " out flags");
+ TEST_EQ(backup_write_called, 1, " Backup still only written once");
+
+ /* Now switch to dev-mode. */
+ iparams.flags = VB_INIT_FLAG_DEV_SWITCH_ON;
+ TestVbInit(0, 0, "Dev mode on");
+ TEST_EQ(shared->recovery_reason, 0, " recovery reason");
+ TEST_EQ(iparams.out_flags,
+ VB_INIT_OUT_CLEAR_RAM |
+ VB_INIT_OUT_ENABLE_DISPLAY |
+ VB_INIT_OUT_ENABLE_USB_STORAGE |
+ VB_INIT_OUT_ENABLE_DEVELOPER |
+ VB_INIT_OUT_ENABLE_ALTERNATE_OS, " out flags");
+ TEST_EQ(shared->flags, VBSD_BOOT_DEV_SWITCH_ON, " shared flags");
+ TEST_EQ(backup_write_called, 1, " Still only one backup");
+
+ /* Now change some params that should be backed up. */
+ VbNvSet(&vnc, VBNV_KERNEL_FIELD, 0xdeadbeef);
+ VbNvSet(&vnc, VBNV_LOCALIZATION_INDEX, 0x5a);
+ VbNvSet(&vnc, VBNV_DEV_BOOT_USB, 1);
+ VbNvSet(&vnc, VBNV_DEV_BOOT_LEGACY, 1);
+ VbNvSet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, 1);
+ /* and some that don't */
+ VbNvSet(&vnc, VBNV_OPROM_NEEDED, 1);
+ VbNvSet(&vnc, VBNV_TRY_B_COUNT, 4);
+ /* Make sure they're clean */
+ VbNvTeardown(&vnc);
+ TestVbInit(0, 0, "Dev mode on");
+ TEST_EQ(shared->recovery_reason, 0, " recovery reason");
+ TEST_EQ(iparams.out_flags,
+ VB_INIT_OUT_CLEAR_RAM |
+ VB_INIT_OUT_ENABLE_DISPLAY |
+ VB_INIT_OUT_ENABLE_USB_STORAGE |
+ VB_INIT_OUT_ENABLE_DEVELOPER, " out flags");
+ TEST_EQ(shared->flags, VBSD_BOOT_DEV_SWITCH_ON, " shared flags");
+ TEST_EQ(backup_write_called, 1, " Once more, one backup");
+
+ /* But if we explictly request a backup, they'll get saved. */
+ VbNvSet(&vnc, VBNV_BACKUP_NVRAM_REQUEST, 1);
+ VbNvTeardown(&vnc);
+ TestVbInit(0, 0, "Dev mode on");
+ TEST_EQ(shared->recovery_reason, 0, " recovery reason");
+ TEST_EQ(iparams.out_flags,
+ VB_INIT_OUT_CLEAR_RAM |
+ VB_INIT_OUT_ENABLE_DISPLAY |
+ VB_INIT_OUT_ENABLE_USB_STORAGE |
+ VB_INIT_OUT_ENABLE_DEVELOPER, " out flags");
+ TEST_EQ(shared->flags, VBSD_BOOT_DEV_SWITCH_ON, " shared flags");
+ TEST_EQ(backup_write_called, 2, " Two backups now");
+ VbNvGet(&vnc, VBNV_BACKUP_NVRAM_REQUEST, &u);
+ TEST_EQ(u, 0, " backup_request cleared");
+ /* Quick check that the non-backed-up stuff is still valid */
+ VbNvGet(&vnc, VBNV_OPROM_NEEDED, &u);
+ TEST_EQ(u, 1, " NV oprom_needed");
+ VbNvGet(&vnc, VBNV_TRY_B_COUNT, &u);
+ TEST_EQ(u, 4, " NV try_b_count");
+ /* But only the stuff we care about was backed up */
+ Memset(&tmp_vnc, 0, sizeof(tmp_vnc));
+ TEST_EQ(0, RestoreNvFromBackup(&tmp_vnc), "read from backup");
+ VbNvGet(&tmp_vnc, VBNV_KERNEL_FIELD, &u);
+ TEST_EQ(u, 0xdeadbeef, " BU kernel field");
+ VbNvGet(&tmp_vnc, VBNV_LOCALIZATION_INDEX, &u);
+ TEST_EQ(u, 0x5a, " BU localization index");
+ VbNvGet(&tmp_vnc, VBNV_DEV_BOOT_USB, &u);
+ TEST_EQ(u, 1, " BU dev_boot_usb");
+ VbNvGet(&tmp_vnc, VBNV_DEV_BOOT_LEGACY, &u);
+ TEST_EQ(u, 1, " BU dev_boot_legacy");
+ VbNvGet(&tmp_vnc, VBNV_DEV_BOOT_SIGNED_ONLY, &u);
+ TEST_EQ(u, 1, " BU dev_boot_signed_only");
+ /* but not the others */
+ VbNvGet(&tmp_vnc, VBNV_OPROM_NEEDED, &u);
+ TEST_EQ(u, 0, " BU oprom_needed");
+ VbNvGet(&tmp_vnc, VBNV_TRY_B_COUNT, &u);
+ TEST_EQ(u, 0, " BU try_b_count");
+
+ /* If we lose the NV storage, the backup bits will be restored */
+ vnc.raw[0] = 0;
+ bu_r = backup_read_called;
+ nv_w = nv_write_called;
+ TestVbInit(0, 0, "Dev mode on");
+ TEST_EQ(shared->recovery_reason, 0, " recovery reason");
+ TEST_EQ(iparams.out_flags,
+ VB_INIT_OUT_CLEAR_RAM |
+ VB_INIT_OUT_ENABLE_DISPLAY |
+ VB_INIT_OUT_ENABLE_USB_STORAGE |
+ VB_INIT_OUT_ENABLE_DEVELOPER, " out flags");
+ TEST_EQ(shared->flags, VBSD_BOOT_DEV_SWITCH_ON, " shared flags");
+ TEST_EQ(backup_write_called, 2, " Still just two backups now");
+ TEST_EQ(backup_read_called, bu_r + 1, " One more backup read");
+ TEST_EQ(nv_write_called, nv_w + 1, " One more NV write");
+ /* The non-backed-up stuff is reset to defaults */
+ VbNvGet(&vnc, VBNV_OPROM_NEEDED, &u);
+ TEST_EQ(u, 0, " NV oprom_needed");
+ VbNvGet(&vnc, VBNV_TRY_B_COUNT, &u);
+ TEST_EQ(u, 0, " NV try_b_count");
+ /* And the backed up stuff is restored */
+ VbNvGet(&vnc, VBNV_KERNEL_FIELD, &u);
+ TEST_EQ(u, 0xdeadbeef, " BU kernel field");
+ VbNvGet(&vnc, VBNV_LOCALIZATION_INDEX, &u);
+ TEST_EQ(u, 0x5a, " BU localization index");
+ VbNvGet(&vnc, VBNV_DEV_BOOT_USB, &u);
+ TEST_EQ(u, 1, " BU dev_boot_usb");
+ VbNvGet(&vnc, VBNV_DEV_BOOT_LEGACY, &u);
+ TEST_EQ(u, 1, " BU dev_boot_legacy");
+ VbNvGet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, &u);
+ TEST_EQ(u, 1, " BU dev_boot_signed_only");
+
+ /*
+ * But if we lose the NV storage and go back to normal mode at the same
+ * time, then the DEV_BOOT_* flags will be cleared.
+ */
+ vnc.raw[0] = 0;
+ bu_r = backup_read_called;
+ nv_w = nv_write_called;
+ iparams.flags = 0;
+ TestVbInit(0, 0, "Back to normal mode");
+ TEST_EQ(shared->recovery_reason, 0, " recovery reason");
+ TEST_EQ(iparams.out_flags, 0, " out flags");
+ TEST_EQ(shared->flags, 0, " shared flags");
+ /* We read twice: once to restore, once for read-prior-to-write */
+ TEST_EQ(backup_read_called, bu_r + 2, " Two more backup reads");
+ TEST_EQ(backup_write_called, 3, " Backup write due clearing DEV_*");
+ TEST_EQ(nv_write_called, nv_w + 1, " One more NV write");
+ /* The non-backed-up stuff is reset to defaults */
+ VbNvGet(&vnc, VBNV_OPROM_NEEDED, &u);
+ TEST_EQ(u, 0, " NV oprom_needed");
+ VbNvGet(&vnc, VBNV_TRY_B_COUNT, &u);
+ TEST_EQ(u, 0, " NV try_b_count");
+ /* And the backed up stuff is restored */
+ VbNvGet(&vnc, VBNV_KERNEL_FIELD, &u);
+ TEST_EQ(u, 0xdeadbeef, " BU kernel field");
+ VbNvGet(&vnc, VBNV_LOCALIZATION_INDEX, &u);
+ TEST_EQ(u, 0x5a, " BU localization index");
+ /* But not the DEV_BOOT_* flags */
+ VbNvGet(&vnc, VBNV_DEV_BOOT_USB, &u);
+ TEST_EQ(u, 0, " BU dev_boot_usb");
+ VbNvGet(&vnc, VBNV_DEV_BOOT_LEGACY, &u);
+ TEST_EQ(u, 0, " BU dev_boot_legacy");
+ VbNvGet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, &u);
+ TEST_EQ(u, 0, " BU dev_boot_signed_only");
+}
+
+
int main(int argc, char *argv[])
{
VbInitTest();
VbInitTestTPM();
+ VbInitTestBackup();
return gTestSuccess ? 0 : 255;
}
diff --git a/utility/crossystem.c b/utility/crossystem.c
index 9a55d17e..5ffb5af1 100644
--- a/utility/crossystem.c
+++ b/utility/crossystem.c
@@ -32,6 +32,8 @@ typedef struct Param {
/* List of parameters, terminated with a param with NULL name */
const Param sys_param_list[] = {
{"arch", IS_STRING, "Platform architecture"},
+ {"backup_nvram_request", CAN_WRITE,
+ "Backup the nvram somewhere at the next boot. Cleared on success."},
{"block_devmode", CAN_WRITE, "Block all use of developer mode"},
{"clear_tpm_owner_request", CAN_WRITE, "Clear TPM owner on next boot"},
{"clear_tpm_owner_done", CAN_WRITE, "Clear TPM owner done"},