diff options
author | Randall Spangler <rspangler@chromium.org> | 2012-05-25 14:57:09 -0700 |
---|---|---|
committer | Randall Spangler <rspangler@chromium.org> | 2012-05-25 15:03:47 -0700 |
commit | 7ecd1d6d3c23b6acb13f90062d062647ddb4fed3 (patch) | |
tree | 796393a5afc4290d974f4ab391a0003c72c3056e | |
parent | e704c712ad473160e97717f139ab3929bcd249c1 (diff) | |
download | chrome-ec-7ecd1d6d3c23b6acb13f90062d062647ddb4fed3.tar.gz |
Add system_is_locked() to prevent sysjump on consumer systems
This returns true when both HW and SW write protect are enabled.
Once WP is enabled, sysjump will be locked out.
system_is_locked() can be used to gate other dangerous-ish commands too.
Signed-off-by: Randall Spangler <rspangler@chromium.org>
BUG=chrome-os-partner:7468
TEST=manual
sysinfo -> unlocked, copy A
sysjump B -> works
flashwp lock
reboot
(make sure flashinfo shows WP asserted and flash locked; note there is a
HW bug on proto1 which makes this flaky)
sysinfo -> locked, copy A
sysjump B -> fails
(remove WP screw)
reboot hard
flashwp unlock
Change-Id: I849b573675c2c1cb4c44b9a05d6973e38247ca23
-rw-r--r-- | board/bds/board.h | 1 | ||||
-rw-r--r-- | board/daisy/board.h | 7 | ||||
-rw-r--r-- | board/snow/board.h | 7 | ||||
-rw-r--r-- | common/main.c | 5 | ||||
-rw-r--r-- | common/system_common.c | 103 | ||||
-rw-r--r-- | include/common.h | 11 | ||||
-rw-r--r-- | include/system.h | 46 |
7 files changed, 123 insertions, 57 deletions
diff --git a/board/bds/board.h b/board/bds/board.h index f22b1e8603..f9d1ef7751 100644 --- a/board/bds/board.h +++ b/board/bds/board.h @@ -10,6 +10,7 @@ /* Optional features */ #define CONFIG_CONSOLE_CMDHELP +#define CONFIG_SYSTEM_UNLOCKED /* Allow dangerous commands */ enum adc_channel { diff --git a/board/daisy/board.h b/board/daisy/board.h index 625df877b5..14270d8906 100644 --- a/board/daisy/board.h +++ b/board/daisy/board.h @@ -17,6 +17,13 @@ /* use I2C for host communication */ #define CONFIG_I2C +/* Allow dangerous commands all the time, since we don't have a write protect + * switch. */ +/* TODO: (crosbug.com/p/9986) This is a serious security hole and should be + * removed in mass production. We add this to allow manual firmware update. + * Once we complete the vboot and autoupdate, we should remove this. */ +#define CONFIG_SYSTEM_UNLOCKED + /* By default, enable all console messages except keyboard */ #define CC_DEFAULT (CC_ALL & ~CC_MASK(CC_KEYSCAN)) diff --git a/board/snow/board.h b/board/snow/board.h index dac98a22ed..0c6d301b9d 100644 --- a/board/snow/board.h +++ b/board/snow/board.h @@ -17,6 +17,13 @@ /* use I2C for host communication */ #define CONFIG_I2C +/* Allow dangerous commands all the time, since we don't have a write protect + * switch. */ +/* TODO: (crosbug.com/p/9986) This is a serious security hole and should be + * removed in mass production. We add this to allow manual firmware update. + * Once we complete the vboot and autoupdate, we should remove this. */ +#define CONFIG_SYSTEM_UNLOCKED + /* By default, enable all console messages except keyboard */ #define CC_DEFAULT (CC_ALL & ~CC_MASK(CC_KEYSCAN)) diff --git a/common/main.c b/common/main.c index 6b4dfb704c..74a698460e 100644 --- a/common/main.c +++ b/common/main.c @@ -110,6 +110,11 @@ int main(void) * Note that steps above here may be done TWICE per boot, once in the * RO image and once in the RW image. */ vboot_init(); + + /* If system is locked, disable system jumps now that vboot has had its + * chance to jump to a RW image. */ + if (system_is_locked()) + system_disable_jump(); #endif /* Initialize other driver modules. These can occur in any order. diff --git a/common/system_common.c b/common/system_common.c index 9d93cdef25..c654143695 100644 --- a/common/system_common.c +++ b/common/system_common.c @@ -5,8 +5,10 @@ /* System module for Chrome EC : common functions */ +#include "board.h" #include "clock.h" #include "console.h" +#include "flash.h" #include "gpio.h" #include "hooks.h" #include "host_command.h" @@ -62,6 +64,30 @@ static struct jump_data * const jdata = static const char * const image_names[] = {"unknown", "RO", "A", "B"}; static enum system_reset_cause_t reset_cause = SYSTEM_RESET_UNKNOWN; static int jumped_to_image; +static int disable_jump; + + +int system_is_locked(void) +{ +#ifdef CONFIG_SYSTEM_UNLOCKED + /* System is explicitly unlocked */ + return 0; + +#elif defined(BOARD_link) && defined(CONFIG_FLASH) + /* On link, unlocked if write protect pin deasserted or flash protect + * lock not applied. */ + int lock = flash_get_protect_lock(); + if (!(lock & FLASH_PROTECT_PIN_ASSERTED) || + !(lock & FLASH_PROTECT_LOCK_APPLIED)) + return 0; + + /* If WP pin is asserted and lock is applied, we're locked */ + return 1; +#else + /* Other configs are locked by default */ + return 1; +#endif +} int system_usable_ram_end(void) @@ -151,6 +177,12 @@ void system_set_reset_cause(enum system_reset_cause_t cause) } +void system_disable_jump(void) +{ + disable_jump = 1; +} + + const char *system_get_reset_cause_string(void) { static const char * const cause_descs[] = { @@ -293,13 +325,26 @@ int system_run_image_copy(enum system_image_copy_t copy, uint32_t base; uint32_t init_addr; - /* TODO: sanity checks (crosbug.com/p/7468) - * - * For this to be allowed either WP must be disabled, or ALL of the - * following must be true: - * - We must currently be running the RO image. - * - We must still be in init (that is, before task_start(). - * - The target image must be A or B. */ + if (system_is_locked()) { + /* System is locked, so disallow jumping between images unless + * this is the initial jump from RO to RW code. */ + + /* Must currently be running the RO image */ + if (system_get_image_copy() != SYSTEM_IMAGE_RO) + return EC_ERROR_ACCESS_DENIED; + + /* Target image must be RW image */ + if (copy != SYSTEM_IMAGE_RW_A && copy != SYSTEM_IMAGE_RW_B) + return EC_ERROR_ACCESS_DENIED; + + /* Can't have already jumped between images */ + if (jumped_to_image) + return EC_ERROR_ACCESS_DENIED; + + /* Jumping must still be enabled */ + if (disable_jump) + return EC_ERROR_ACCESS_DENIED; + } /* Load the appropriate reset vector */ base = get_base(copy); @@ -316,7 +361,7 @@ int system_run_image_copy(enum system_image_copy_t copy, jump_to_image(init_addr, recovery_required); /* Should never get here */ - return EC_ERROR_UNIMPLEMENTED; + return EC_ERROR_UNKNOWN; } @@ -451,8 +496,9 @@ static int command_sysinfo(int argc, char **argv) ccprintf("Last reset: %d (%s)\n", system_get_reset_cause(), system_get_reset_cause_string()); - ccprintf("Copy: %s\n", system_get_image_copy_string()); - ccprintf("Jump: %s\n", system_jumped_to_this_image() ? "yes" : "no"); + ccprintf("Copy: %s\n", system_get_image_copy_string()); + ccprintf("Jumped: %s\n", system_jumped_to_this_image() ? "yes" : "no"); + ccprintf("Locked: %s\n", system_is_locked() ? "yes" : "no"); return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(sysinfo, command_sysinfo, @@ -530,8 +576,9 @@ static int command_sysjump(int argc, char **argv) uint32_t addr; char *e; - /* TODO: (crosbug.com/p/7468) For this command to be allowed, WP must - * be disabled. */ + /* Command is only allowed on an unlocked system */ + if (system_is_locked()) + return EC_ERROR_ACCESS_DENIED; if (argc < 2) return EC_ERROR_PARAM_COUNT; @@ -659,26 +706,16 @@ int host_command_get_board_version(uint8_t *data, int *resp_size) DECLARE_HOST_COMMAND(EC_CMD_GET_BOARD_VERSION, host_command_get_board_version); -#ifdef CONFIG_REBOOT_EC -static void clean_busy_bits(void) { -#ifdef CONFIG_LPC - host_send_result(0, EC_RES_SUCCESS); - host_send_result(1, EC_RES_SUCCESS); -#endif -} - int host_command_reboot(uint8_t *data, int *resp_size) { enum system_image_copy_t copy; + struct ec_params_reboot_ec *p = (struct ec_params_reboot_ec *)data; + int recovery_request = p->reboot_flags & EC_CMD_REBOOT_BIT_RECOVERY; - struct ec_params_reboot_ec *p = - (struct ec_params_reboot_ec *)data; - - int recovery_request = p->reboot_flags & - EC_CMD_REBOOT_BIT_RECOVERY; - - /* TODO: (crosbug.com/p/7468) For this command to be allowed, WP must - * be disabled. */ + /* This command is only allowed on unlocked systems, because jumping + * directly to another image bypasses verified boot. */ + if (system_is_locked()) + return EC_RES_ACCESS_DENIED; switch (p->target) { case EC_IMAGE_RO: @@ -694,8 +731,13 @@ int host_command_reboot(uint8_t *data, int *resp_size) return EC_RES_ERROR; } - clean_busy_bits(); - CPUTS("Executing host reboot command\n"); +#ifdef CONFIG_LPC + /* Clean busy bits on host */ + host_send_result(0, EC_RES_SUCCESS); + host_send_result(1, EC_RES_SUCCESS); +#endif + + CPUTS("[Executing host reboot command]\n"); system_run_image_copy(copy, recovery_request); /* We normally never get down here, because we'll have jumped to @@ -707,4 +749,3 @@ int host_command_reboot(uint8_t *data, int *resp_size) return EC_RES_ERROR; } DECLARE_HOST_COMMAND(EC_CMD_REBOOT_EC, host_command_reboot); -#endif /* CONFIG_REBOOT_EC */ diff --git a/include/common.h b/include/common.h index 91a1a126a1..2eb2157247 100644 --- a/include/common.h +++ b/include/common.h @@ -10,15 +10,6 @@ #include <stdint.h> - -/* TODO: This is a serious security hole and should be removed in mass - * production. We add this to allow manual firmware update. - * Once we complete the vboot and autoupdate, we should remove this. - * https://code.google.com/p/chrome-os-partner/issues/detail?id=8391 - */ -#define CONFIG_REBOOT_EC - - /* List of common error codes that can be returned */ enum ec_error_list { /* Success - no error */ @@ -35,6 +26,8 @@ enum ec_error_list { EC_ERROR_INVAL = 5, /* Already in use */ EC_ERROR_BUSY = 6, + /* Access denied */ + EC_ERROR_ACCESS_DENIED = 7, /* Invalid console command param (PARAMn means parameter n is bad) */ EC_ERROR_PARAM1 = 11, EC_ERROR_PARAM2 = 12, diff --git a/include/system.h b/include/system.h index 33297dbc3f..fb9c802b8f 100644 --- a/include/system.h +++ b/include/system.h @@ -45,19 +45,31 @@ int system_common_pre_init(void); * the cause is not known. */ enum system_reset_cause_t system_get_reset_cause(void); -/* returns a Boolean indicating if BIOS should come up in recovery mode */ +/* Return a Boolean indicating if BIOS should come up in recovery mode */ int system_get_recovery_required(void); /* Record the cause of the last reset. */ void system_set_reset_cause(enum system_reset_cause_t cause); -/* Returns a text description of the last reset cause. */ +/* Return a text description of the last reset cause. */ const char *system_get_reset_cause_string(void); -/* Returns the image copy which is currently running. */ +/* Return non-zero if the system is locked down for normal consumer use. + * Potentially-dangerous developer and/or factory commands must be disabled + * unless this command returns 0. + * + * This should be controlled by the same mechanism which write-protects the + * read-only image (so that the only way to unlock the system is to unprotect + * the read-only image). */ +int system_is_locked(void); + +/* Disable jumping between images for the rest of this boot. */ +void system_disable_jump(void); + +/* Return the image copy which is currently running. */ enum system_image_copy_t system_get_image_copy(void); -/* Returns non-zero if the system has switched between image copies at least +/* Return non-zero if the system has switched between image copies at least * once since the last real boot. */ int system_jumped_to_this_image(void); @@ -75,20 +87,20 @@ int system_add_jump_tag(uint16_t tag, int version, int size, const void *data); * NULL if no matching tag is found. */ const uint8_t *system_get_jump_tag(uint16_t tag, int *version, int *size); -/* Returns the address just past the last usable byte in RAM. */ +/* Return the address just past the last usable byte in RAM. */ int system_usable_ram_end(void); /* Returns true if the given range is overlapped with the active image. */ int system_unsafe_to_overwrite(uint32_t offset, uint32_t size); -/* Returns a text description of the image copy which is currently running. */ +/* Return a text description of the image copy which is currently running. */ const char *system_get_image_copy_string(void); -/* Jumps to the specified image copy. */ +/* Jump to the specified image copy. */ int system_run_image_copy(enum system_image_copy_t copy, int recovery_request); -/* Returns the version string for an image copy, or an empty string if +/* Return the version string for an image copy, or an empty string if * error. If copy==SYSTEM_IMAGE_UNKNOWN, returns the version for the * currently-running image. */ const char *system_get_version(enum system_image_copy_t copy); @@ -97,25 +109,24 @@ const char *system_get_version(enum system_image_copy_t copy); * board-dependent; see enum board_version in board.h for known versions. */ int system_get_board_version(void); -/* Returns information about the build including the version - * the build date and user/machine. - */ +/* Return information about the build including the version, build date and + * user/machine which performed the build. */ const char *system_get_build_info(void); -/* Resets the system. If is_hard, performs a hard reset, which cuts power to +/* Reset the system. If is_hard, performs a hard reset, which cuts power to * the entire system; else performs a soft reset (which resets the core and * on-chip peripherals, without actually cutting power to the chip). */ void system_reset(int is_hard); -/* Sets a scratchpad register to the specified value. The scratchpad +/* Set a scratchpad register to the specified value. The scratchpad * register must maintain its contents across a software-requested * warm reset. */ int system_set_scratchpad(uint32_t value); -/* Returns the current scratchpad register value. */ +/* Return the current scratchpad register value. */ uint32_t system_get_scratchpad(void); -/* Returns the chip info */ +/* Return the chip info */ const char *system_get_chip_vendor(void); const char *system_get_chip_name(void); const char *system_get_chip_revision(void); @@ -123,10 +134,11 @@ const char *system_get_chip_revision(void); /* TODO: request sleep. How do we want to handle transitioning * to/from low-power states? */ -/* put the system in hibernation for the specified duration */ +/* Put the EC in hibernate (lowest EC power state) for the specified + * duration. Note that this is NOT the same as chipset S4/hibernate. */ void system_hibernate(uint32_t seconds, uint32_t microseconds); -/* minimum duration to get proper hibernation */ +/* Minimum duration to get proper hibernation */ #define SYSTEM_HIB_MINIMUM_DURATION 0, 1000 #endif /* __CROS_EC_SYSTEM_H */ |