diff options
-rw-r--r-- | board/cr50/board.c | 16 | ||||
-rw-r--r-- | board/cr50/scratch_reg1.h | 35 | ||||
-rw-r--r-- | board/cr50/wp.c | 105 | ||||
-rw-r--r-- | chip/g/system.c | 52 | ||||
-rw-r--r-- | include/system.h | 6 |
5 files changed, 171 insertions, 43 deletions
diff --git a/board/cr50/board.c b/board/cr50/board.c index 956d7283c1..ab3f959a62 100644 --- a/board/cr50/board.c +++ b/board/cr50/board.c @@ -22,6 +22,7 @@ #include "nvmem.h" #include "rdd.h" #include "registers.h" +#include "scratch_reg1.h" #include "signed_header.h" #include "spi.h" #include "system.h" @@ -80,18 +81,6 @@ uint32_t nvmem_user_sizes[NVMEM_NUM_USERS] = { static uint32_t board_properties; static uint8_t reboot_request_posted; -/* - * Bit assignments of the LONG_LIFE_SCRATCH1 register. This register survives - * all kinds of resets, it is cleared only on the Power ON event. - */ -#define BOARD_SLAVE_CONFIG_SPI (1 << 0) /* TPM uses SPI interface */ -#define BOARD_SLAVE_CONFIG_I2C (1 << 1) /* TPM uses I2C interface */ -#define BOARD_USB_AP (1 << 2) /* One of the USB PHYs is */ - /* connected to the AP */ - -/* TODO(crosbug.com/p/56945): Remove when sys_rst_l has an external pullup */ -#define BOARD_NEEDS_SYS_RST_PULL_UP (1 << 5) /* Add a pullup to sys_rst_l */ -#define BOARD_USE_PLT_RESET (1 << 6) /* Platform reset exists */ int board_has_ap_usb(void) { return !!(board_properties & BOARD_USB_AP); @@ -325,9 +314,6 @@ static void board_init(void) /* Initialize NvMem partitions */ nvmem_init(); - /* Enable write protect on production images. Disable it on dev */ - GREG32(RBOX, EC_WP_L) = !console_is_restricted(); - /* Indication that firmware is running, for debug purposes. */ GREG32(PMU, PWRDN_SCRATCH16) = 0xCAFECAFE; } diff --git a/board/cr50/scratch_reg1.h b/board/cr50/scratch_reg1.h new file mode 100644 index 0000000000..b0c3a12431 --- /dev/null +++ b/board/cr50/scratch_reg1.h @@ -0,0 +1,35 @@ +/* + * Copyright 2016 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef __EC_BOARD_CR50_SCRATCH_REG1_H +#define __EC_BOARD_CR50_SCRATCH_REG1_H + +/* + * Bit assignments of the LONG_LIFE_SCRATCH1 register. This register survives + * all kinds of resets, it is cleared only on the Power ON event. + */ +#define BOARD_SLAVE_CONFIG_SPI (1 << 0) /* TPM uses SPI interface */ +#define BOARD_SLAVE_CONFIG_I2C (1 << 1) /* TPM uses I2C interface */ +#define BOARD_USB_AP (1 << 2) /* One of the USB PHYs is */ + /* connected to the AP */ +/* + * This gap is left to enusre backwards compatibility with the earliest cr50 + * code releases. It will be possible to safely reuse this gap if and when the + * rest of the bits are taken. + */ + +/* TODO(crosbug.com/p/56945): Remove when sys_rst_l has an external pullup */ +#define BOARD_NEEDS_SYS_RST_PULL_UP (1 << 5) /* Add a pullup to sys_rst_l */ +#define BOARD_USE_PLT_RESET (1 << 6) /* Platform reset exists */ + +/* + * Bits to store console and write protect bit states across deep sleep and + * resets. + */ +#define BOARD_CONSOLE_UNLOCKED (1 << 7) +#define BOARD_WP_ASSERTED (1 << 8) + +#endif /* ! __EC_BOARD_CR50_SCRATCH_REG1_H */ diff --git a/board/cr50/wp.c b/board/cr50/wp.c index 28268f4cca..65e2344aee 100644 --- a/board/cr50/wp.c +++ b/board/cr50/wp.c @@ -10,6 +10,7 @@ #include "hooks.h" #include "nvmem.h" #include "registers.h" +#include "scratch_reg1.h" #include "system.h" #include "task.h" #include "timer.h" @@ -18,16 +19,36 @@ #define CPRINTS(format, args...) cprints(CC_RBOX, format, ## args) #define CPRINTF(format, args...) cprintf(CC_RBOX, format, ## args) +static void set_wp_state(int asserted) +{ + /* Enable writing to the long life register */ + GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 1); + + if (asserted) { + GREG32(PMU, LONG_LIFE_SCRATCH1) |= BOARD_WP_ASSERTED; + GREG32(RBOX, EC_WP_L) = 0; + } else { + GREG32(PMU, LONG_LIFE_SCRATCH1) &= ~BOARD_WP_ASSERTED; + GREG32(RBOX, EC_WP_L) = 1; + } + + /* Disable writing to the long life register */ + GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 0); +} + static int command_wp(int argc, char **argv) { int val; if (argc > 1) { - if (!parse_bool(argv[1], &val)) - return EC_ERROR_PARAM1; + if (console_is_restricted()) { + ccprintf("Console is locked, no parameters allowed\n"); + } else { + if (!parse_bool(argv[1], &val)) + return EC_ERROR_PARAM1; - /* Invert, because active low */ - GREG32(RBOX, EC_WP_L) = !val; + set_wp_state(!!val); + } } /* Invert, because active low */ @@ -37,24 +58,53 @@ static int command_wp(int argc, char **argv) return EC_SUCCESS; } -DECLARE_CONSOLE_COMMAND(wp, command_wp, - "[<BOOLEAN>]", - "Get/set the flash HW write-protect signal"); +DECLARE_SAFE_CONSOLE_COMMAND(wp, command_wp, + "[<BOOLEAN>]", + "Get/set the flash HW write-protect signal"); /* When the system is locked down, provide a means to unlock it */ #ifdef CONFIG_RESTRICTED_CONSOLE_COMMANDS +#define LOCK_ENABLED 1 + /* Hand-built images may be initially unlocked; Buildbot images are not. */ #ifdef CR50_DEV -static int console_restricted_state; +static int console_restricted_state = !LOCK_ENABLED; #else -static int console_restricted_state = 1; +static int console_restricted_state = LOCK_ENABLED; #endif +static void set_console_lock_state(int lock_state) +{ + console_restricted_state = lock_state; + + /* + * Assert WP unconditionally on locked console. Keep this invocation + * separate, as it will also enable/disable writes into + * LONG_LIFE_SCRATCH1 + */ + if (lock_state == LOCK_ENABLED) + set_wp_state(1); + + /* Enable writing to the long life register */ + GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 1); + + /* Save the lock state in long life scratch */ + if (lock_state == LOCK_ENABLED) + GREG32(PMU, LONG_LIFE_SCRATCH1) &= ~BOARD_CONSOLE_UNLOCKED; + else + GREG32(PMU, LONG_LIFE_SCRATCH1) |= BOARD_CONSOLE_UNLOCKED; + + /* Disable writing to the long life register */ + GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 0); + + CPRINTS("The console is %s", + lock_state == LOCK_ENABLED ? "locked" : "unlocked"); +} + static void lock_the_console(void) { - CPRINTS("The console is locked"); - console_restricted_state = 1; + set_console_lock_state(LOCK_ENABLED); } static void unlock_the_console(void) @@ -71,12 +121,41 @@ static void unlock_the_console(void) * bet for fixing the problem. */ CPRINTS("%s: Couldn't wipe nvmem! (rc %d)", __func__, rc); + cflush(); system_reset(SYSTEM_RESET_HARD); } - CPRINTS("TPM is erased, console is unlocked"); - console_restricted_state = 0; + CPRINTS("TPM is erased"); + set_console_lock_state(!LOCK_ENABLED); +} + +static void init_console_lock_and_wp(void) +{ + /* + * On an unexpected reboot or a system rollback reset the console and + * write protect states. + */ + if (system_rollback_detected() || + !(system_get_reset_flags() & RESET_FLAG_HIBERNATE)) { + /* Reset the console lock to the default value */ + set_console_lock_state(console_restricted_state); + + /* Always assert WP on H1 cold resets, reboots or fallbacks. */ + set_wp_state(1); + return; + } + + if (GREG32(PMU, LONG_LIFE_SCRATCH1) & BOARD_CONSOLE_UNLOCKED) + set_console_lock_state(!LOCK_ENABLED); + else + set_console_lock_state(LOCK_ENABLED); + + if (GREG32(PMU, LONG_LIFE_SCRATCH1) & BOARD_WP_ASSERTED) + set_wp_state(1); + else + set_wp_state(0); } +DECLARE_HOOK(HOOK_INIT, init_console_lock_and_wp, HOOK_PRIO_DEFAULT); int console_is_restricted(void) { diff --git a/chip/g/system.c b/chip/g/system.c index 5ab69abda1..a6a67cc5f1 100644 --- a/chip/g/system.c +++ b/chip/g/system.c @@ -310,7 +310,7 @@ static int a_is_newer_than_b(const struct SignedHeader *a, * apparently failing image from being considered as a candidate to load and * run on the following reboots. */ -static int corrupt_other_header(volatile struct SignedHeader *header) +static int corrupt_header(volatile struct SignedHeader *header) { int rv; const char zero[4] = {}; /* value to write to magic. */ @@ -344,19 +344,15 @@ static int corrupt_other_header(volatile struct SignedHeader *header) */ #define RW_BOOT_MAX_RETRY_COUNT 5 -int system_process_retry_counter(void) +/* + * Check if the current running image is newer. Set the passed in pointer, if + * supplied, to point to the newer image in case the running image is the + * older one. + */ +static int current_image_is_newer(struct SignedHeader **newer_image) { - unsigned retry_counter; struct SignedHeader *me, *other; - retry_counter = GREG32(PMU, LONG_LIFE_SCRATCH0); - system_clear_retry_counter(); - - ccprintf("%s:retry counter %d\n", __func__, retry_counter); - - if (retry_counter <= RW_BOOT_MAX_RETRY_COUNT) - return EC_SUCCESS; - if (system_get_image_copy() == SYSTEM_IMAGE_RW) { me = (struct SignedHeader *) get_program_memory_addr(SYSTEM_IMAGE_RW); @@ -369,17 +365,43 @@ int system_process_retry_counter(void) get_program_memory_addr(SYSTEM_IMAGE_RW); } - if (a_is_newer_than_b(me, other)) { + if (a_is_newer_than_b(me, other)) + return 1; + + if (newer_image) + *newer_image = other; + return 0; +} + +int system_rollback_detected(void) +{ + return !current_image_is_newer(NULL); +} + +int system_process_retry_counter(void) +{ + unsigned retry_counter; + struct SignedHeader *newer_image; + + retry_counter = GREG32(PMU, LONG_LIFE_SCRATCH0); + system_clear_retry_counter(); + + ccprintf("%s:retry counter %d\n", __func__, retry_counter); + + if (retry_counter <= RW_BOOT_MAX_RETRY_COUNT) + return EC_SUCCESS; + + if (current_image_is_newer(&newer_image)) { ccprintf("%s: " "this is odd, I am newer, but retry counter was %d\n", __func__, retry_counter); return EC_SUCCESS; } /* - * let's corrupt the "other" guy so that the next restart is happening - * straight into this version. + * let's corrupt the newer image so that the next restart is happening + * straight into the current version. */ - return corrupt_other_header(other); + return corrupt_header(newer_image); } int system_rolling_reboot_suspected(void) diff --git a/include/system.h b/include/system.h index c8b7c8ee97..4df0fb1bad 100644 --- a/include/system.h +++ b/include/system.h @@ -488,4 +488,10 @@ void system_clear_retry_counter(void); */ int system_rolling_reboot_suspected(void); +/** + * Compare the rw headers to check if there was a rollback. + * + * @return a boolean, set to True if a rollback is detected. + */ +int system_rollback_detected(void); #endif /* __CROS_EC_SYSTEM_H */ |