summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2016-11-16 15:37:44 -0800
committerchrome-bot <chrome-bot@chromium.org>2016-11-19 00:14:24 -0800
commit93388bc758cf7c2cfc70f63fbe42c25b1ed7fb38 (patch)
tree49310e932f37812d771166b9f3778906f37f232f /common
parent7f8ad649dd1a417a746739e86bce7dc3f0a40c1b (diff)
downloadchrome-ec-93388bc758cf7c2cfc70f63fbe42c25b1ed7fb38.tar.gz
Cr50: Prevent rebooting when unlocking the console
When the console is unlocked, the function nvmem_wipe_or_reboot() is called. This holds the EC in reset, clears nvmem, resets the TPM task, then releases the EC. Nothing about that should cause the Cr50 to reboot, but it was happening anyway. This CL addresses several subtle problems. First, holding the EC in reset invoked the sys_rst_asserted() interrupt handler, triggering extra (and early) calls to tpm_reset(). That should wait until after nvmem is cleared, and only be called once. Second, the intentional call to tpm_reset() caused the current (HOOKS) task to wait for the operation to finish, but it didn't wait long enough (recreating the endorsement certs can take over a second). When the task_wake_event() returned, a timeout was indicated in addition to the completion event. Third, because we checked for the timeout first, we reported an error even though tpm_reset() completed successfully, just slower than we expected. We didn't get the timeout event before it completed because the TPM task runs at a higher priority. This CL addresses all of these cases, and makes wiping nvmem the responsibility of the TPM task as well, so that it can do it when it's ready. Note that the EC (and thus AP too) will be held in reset while nvmem is erased. BUG=chrome-os-partner:59902 BRANCH=none TEST=make buildall, manual tests From the Cr50 console, run the "lock on" and "lock off" commands. Try it both with and without the battery present. Observe that the Cr50 no longer reboots just because the console unlocks. Change-Id: I65a342502718acc5b9bda8c6f28dcd27e8f027f7 Signed-off-by: Bill Richardson <wfrichar@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/411379 Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
Diffstat (limited to 'common')
-rw-r--r--common/tpm_registers.c84
1 files changed, 64 insertions, 20 deletions
diff --git a/common/tpm_registers.c b/common/tpm_registers.c
index 1dbd79f299..62e4414a52 100644
--- a/common/tpm_registers.c
+++ b/common/tpm_registers.c
@@ -640,33 +640,55 @@ static void call_extension_command(struct tpm_cmd_header *tpmh,
#endif
/* Event (to TPM task) to request reset, or (from TPM task) on completion. */
-#define TPM_EVENT_RESET (TASK_EVENT_CUSTOM(1))
+#define TPM_EVENT_RESET TASK_EVENT_CUSTOM(1 << 0)
-/* Calling task to notify when the TPM reset has completed */
+/* Calling task (singular) to notify when the TPM reset has completed */
static __initialized task_id_t waiting_for_reset = TASK_ID_INVALID;
-int tpm_reset(void)
+/* Return value from blocking tpm_reset() call */
+static __preserved int wipe_result;
+
+/* Did tpm_reset() request nvmem wipe? (intentionally cleared on reset) */
+static int wipe_requested;
+
+int tpm_reset(int wait_until_done, int wipe_nvmem_first)
{
uint32_t evt;
- cprints(CC_TASK, "%s", __func__);
+ cprints(CC_TASK, "%s(%d, %d)", __func__,
+ wait_until_done, wipe_nvmem_first);
+
+ if (reset_in_progress) {
+ cprints(CC_TASK, "%s: already scheduled", __func__);
+ return EC_ERROR_BUSY;
+ }
+
+ reset_in_progress = 1;
+ wipe_result = EC_SUCCESS;
+
+ /* We can't change our minds about wiping. */
+ wipe_requested |= wipe_nvmem_first;
+ /* Ask the TPM task to reset itself */
task_set_event(TASK_ID_TPM, TPM_EVENT_RESET, 0);
+ if (!wait_until_done)
+ return EC_SUCCESS;
+
if (in_interrupt_context() ||
task_get_current() == TASK_ID_TPM)
- return 0; /* Can't sleep. Clown'll eat me. */
+ return EC_ERROR_BUSY; /* Can't sleep. Clown'll eat me. */
- /* Try to wait until the TPM is reset, but timeout eventually */
+ /* Completion could take a while, if other things have priority */
waiting_for_reset = task_get_current();
- evt = task_wait_event_mask(TPM_EVENT_RESET, SECOND);
+ evt = task_wait_event_mask(TPM_EVENT_RESET, 5 * SECOND);
- /* Timeout is bad */
- if (evt & TASK_EVENT_TIMER)
- return -1;
+ /* We were notified of completion */
+ if (evt & TPM_EVENT_RESET)
+ return wipe_result;
- /* Otherwise, good */
- return 1;
+ /* Timeout is bad */
+ return EC_ERROR_TIMEOUT;
}
int tpm_is_resetting(void)
@@ -674,12 +696,25 @@ int tpm_is_resetting(void)
return reset_in_progress;
}
-static void tpm_reset_now(void)
+static void tpm_reset_now(int wipe_first)
{
- reset_in_progress = 1;
-
/* This is more related to TPM task activity than TPM transactions */
- cprints(CC_TASK, "%s", __func__);
+ cprints(CC_TASK, "%s(%d)", __func__, wipe_first);
+
+ if (wipe_first) {
+ /*
+ * Blindly zapping the TPM space while the AP is awake and
+ * poking at it will bork the TPM task and the AP itself, so
+ * force the whole system off by holding the EC in reset.
+ */
+ cprints(CC_TASK, "%s: force EC off", __func__);
+ assert_ec_rst();
+
+ /* Now wipe nvmem */
+ wipe_result = nvmem_setup(0);
+ } else {
+ wipe_result = EC_SUCCESS;
+ }
/*
* Clear the TPM library's zero-init data. Note that the linker script
@@ -691,10 +726,11 @@ static void tpm_reset_now(void)
(uintptr_t)(&__bss_libtpm2_start));
/*
- * NOTE: Any initialized variables in this file must be placed in a
- * separate section (NOT .data). If they need resetting, do so here.
+ * NOTE: If any __initialized variables need reinitializing after
+ * reset, this is the place to do it.
*/
+
/* Re-initialize our registers */
tpm_init();
@@ -703,12 +739,20 @@ static void tpm_reset_now(void)
task_set_event(waiting_for_reset, TPM_EVENT_RESET, 0);
waiting_for_reset = TASK_ID_INVALID;
}
+
+ if (wipe_first) {
+ /* Allow AP & EC to boot again */
+ cprints(CC_TASK, "%s: allow EC to boot", __func__);
+ deassert_ec_rst();
+ }
+
+ cprints(CC_TASK, "%s: done", __func__);
reset_in_progress = 0;
}
void tpm_task(void)
{
- tpm_reset_now();
+ tpm_reset_now(0);
while (1) {
uint8_t *response;
unsigned response_size;
@@ -719,7 +763,7 @@ void tpm_task(void)
/* Wait for the next command event */
evt = task_wait_event(-1);
if (evt & TPM_EVENT_RESET) {
- tpm_reset_now();
+ tpm_reset_now(wipe_requested);
continue;
}
tpmh = (struct tpm_cmd_header *)tpm_.regs.data_fifo;