diff --git a/board/cr50/board.c b/board/cr50/board.c index 6f753559ee..7ce2633e76 100644 --- a/board/cr50/board.c +++ b/board/cr50/board.c @@ -359,7 +359,7 @@ void sys_rst_asserted(enum gpio_signal signal) system_reset(SYSTEM_RESET_HARD); /* This will never return. */ /* Re-initialize the TPM software state */ - tpm_reset(); + tpm_reset(0, 0); } void assert_sys_rst(void) @@ -414,27 +414,6 @@ int is_ec_rst_asserted(void) return GREAD(RBOX, ASSERT_EC_RST); } -void nvmem_wipe_or_reboot(void) -{ - /* - * 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. - */ - assert_ec_rst(); - - /* - * If we can't clear the NVMEM or can't reset the TPM task, something - * is unexpectedly wrong. To be safe, let's reboot the Cr50 (which also - * reboots the EC and AP). - */ - if (nvmem_setup(0) != EC_SUCCESS || tpm_reset() != 1) - system_reset(SYSTEM_RESET_HARD); - - /* Wipe & reset is complete. Allow the EC and AP to reboot */ - deassert_ec_rst(); -} - void nvmem_compute_sha(uint8_t *p_buf, int num_bytes, uint8_t *p_sha, int sha_len) { diff --git a/board/cr50/wp.c b/board/cr50/wp.c index d667ecc74e..69b88c4a71 100644 --- a/board/cr50/wp.c +++ b/board/cr50/wp.c @@ -13,6 +13,7 @@ #include "system.h" #include "task.h" #include "timer.h" +#include "tpm_registers.h" #define CPRINTS(format, args...) cprints(CC_RBOX, format, ## args) #define CPRINTF(format, args...) cprintf(CC_RBOX, format, ## args) @@ -58,7 +59,21 @@ static void lock_the_console(void) static void unlock_the_console(void) { - nvmem_wipe_or_reboot(); + int rc; + + /* Wipe the TPM's memory and reset the TPM task. */ + rc = tpm_reset(1, 1); + if (rc != EC_SUCCESS) { + /* + * If anything goes wrong (which is unlikely), we REALLY don't + * want to unlock the console. It's possible to fail without + * the TPM task ever running, so rebooting is probably our best + * bet for fixing the problem. + */ + CPRINTS("%s: Couldn't wipe nvmem! (rc %d)", __func__, rc); + system_reset(SYSTEM_RESET_HARD); + } + CPRINTS("TPM is erased, console is unlocked"); console_restricted_state = 0; } 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); + + /* We were notified of completion */ + if (evt & TPM_EVENT_RESET) + return wipe_result; /* Timeout is bad */ - if (evt & TASK_EVENT_TIMER) - return -1; - - /* Otherwise, good */ - return 1; + 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; diff --git a/include/nvmem.h b/include/nvmem.h index 354e763c8d..740c845d9d 100644 --- a/include/nvmem.h +++ b/include/nvmem.h @@ -177,11 +177,4 @@ int nvmem_setup(uint8_t version); void nvmem_compute_sha(uint8_t *p_buf, int num_bytes, uint8_t *p_sha, int sha_len); -/* - * Erase and reformat the entire nvmem storage space. This returns only if it - * was successful. If it fails, we can't be certain of the state of the system, - * so it should do a hard reboot to be safe. - */ -void nvmem_wipe_or_reboot(void); - #endif /* __CROS_EC_NVMEM_UTILS_H */ diff --git a/include/tpm_registers.h b/include/tpm_registers.h index dfdfefdc34..887cc163cd 100644 --- a/include/tpm_registers.h +++ b/include/tpm_registers.h @@ -31,20 +31,21 @@ typedef void (*interface_restart_func)(void); void tpm_register_interface(interface_restart_func interface_restart); /* - * Reset the TPM. This sends a request to the TPM task, so that the reset can - * happen when the TPM task finishes whatever it's doing at the moment. + * This requests the TPM task to reset itself. * - * Returns 0 if the request was made, but we can't wait for it to complete - * because we're in interrupt context or something similar. Otherwise, it - * blocks and returns 1 after the TPM has been cleared, or returns -1 if the - * request timed out. + * If wait_until_done is false, it returns EC_SUCCESS immediately. Otherwise it + * returns EC_SUCCESS after the reset has completed, or an error code on + * failure. + * + * If wipe_nvmem_first is true, the EC and AP will be forced off and TPM memory + * will be erased before the TPM task is reset. */ -int tpm_reset(void); +int tpm_reset(int wait_until_done, int wipe_nvmem_first); /* - * Return true if tpm is being reset. Usually this helps to avoid unnecessary - * extra reset early at startup time, when TPM could be busy installing - * endorsement certificates. + * Return true if the TPM is being reset. Usually this helps to avoid + * unnecessary extra reset early at startup time, when TPM could be busy + * installing endorsement certificates. */ int tpm_is_resetting(void);