diff options
-rw-r--r-- | common/build.mk | 1 | ||||
-rw-r--r-- | common/firmware_image.S | 8 | ||||
-rw-r--r-- | common/firmware_image.lds.S | 6 | ||||
-rw-r--r-- | common/fmap.c | 32 | ||||
-rw-r--r-- | common/rollback.c | 179 | ||||
-rw-r--r-- | common/rwsig.c | 18 | ||||
-rw-r--r-- | common/system.c | 9 | ||||
-rw-r--r-- | common/version.c | 5 | ||||
-rw-r--r-- | include/config.h | 11 | ||||
-rw-r--r-- | include/rollback.h | 32 | ||||
-rw-r--r-- | include/system.h | 9 | ||||
-rw-r--r-- | include/version.h | 3 |
12 files changed, 310 insertions, 3 deletions
diff --git a/common/build.mk b/common/build.mk index 6c13a9c4d0..630e9f9eaa 100644 --- a/common/build.mk +++ b/common/build.mk @@ -78,6 +78,7 @@ common-$(CONFIG_PSTORE)+=pstore_commands.o common-$(CONFIG_PWM)+=pwm.o common-$(CONFIG_PWM_KBLIGHT)+=pwm_kblight.o common-$(CONFIG_RSA)+=rsa.o +common-$(CONFIG_ROLLBACK)+=rollback.o common-$(CONFIG_RWSIG)+=rwsig.o common-$(CONFIG_MATH_UTIL)+=math_util.o common-$(CONFIG_SHA1)+= sha1.o diff --git a/common/firmware_image.S b/common/firmware_image.S index 97a7123f49..ed78bad3ce 100644 --- a/common/firmware_image.S +++ b/common/firmware_image.S @@ -7,6 +7,7 @@ */ #include "config.h" +#include "rollback.h" #define FW_FILE(builddir,proj,sect,suffix,ext) \ builddir##/##sect##/##proj##.##sect##suffix##.flat##ext @@ -29,6 +30,13 @@ .incbin STRINGIFY(FINAL_OUTDIR/key.vbpubk2) #endif +#ifdef CONFIG_ROLLBACK +/* Note: matches struct rollback_data in common/rollback.c. */ +.section .image.ROLLBACK, "a" +.long CONFIG_ROLLBACK_VERSION +.long CROS_EC_ROLLBACK_COOKIE +#endif + /* Shared objects library */ #ifdef CONFIG_SHAREDLIB .section .image.libsharedobjs, "ax" diff --git a/common/firmware_image.lds.S b/common/firmware_image.lds.S index 85d4a56aa1..665743d878 100644 --- a/common/firmware_image.lds.S +++ b/common/firmware_image.lds.S @@ -33,6 +33,12 @@ SECTIONS } > FLASH =0xff #endif +#ifdef CONFIG_ROLLBACK + .image.ROLLBACK : AT(CONFIG_PROGRAM_MEMORY_BASE + CONFIG_ROLLBACK_OFF) { + *(.image.ROLLBACK) + } > FLASH =0xff +#endif + #ifdef CONFIG_SHAREDLIB .image.libsharedobjs : AT(CONFIG_PROGRAM_MEMORY_BASE + \ CONFIG_SHAREDLIB_MEM_OFF) { diff --git a/common/fmap.c b/common/fmap.c index c86d28b5ca..eb2a883b5f 100644 --- a/common/fmap.c +++ b/common/fmap.c @@ -63,11 +63,21 @@ struct fmap_area_header { } __packed; #ifdef CONFIG_RWSIG_TYPE_RWSIG -#define NUM_EC_FMAP_AREAS 9 +#define NUM_EC_FMAP_AREAS_RWSIG 2 #else -#define NUM_EC_FMAP_AREAS 7 +#define NUM_EC_FMAP_AREAS_RWSIG 0 #endif +#ifdef CONFIG_ROLLBACK +#define NUM_EC_FMAP_AREAS_ROLLBACK 1 +#else +#define NUM_EC_FMAP_AREAS_ROLLBACK 0 +#endif + +#define NUM_EC_FMAP_AREAS (7 + \ + NUM_EC_FMAP_AREAS_RWSIG + \ + NUM_EC_FMAP_AREAS_ROLLBACK) + const struct _ec_fmap { struct fmap_header header; struct fmap_area_header area[NUM_EC_FMAP_AREAS]; @@ -178,6 +188,24 @@ const struct _ec_fmap { .area_size = sizeof(current_image_data.version), .area_flags = FMAP_AREA_STATIC, }, +#ifdef CONFIG_ROLLBACK + { + /* + * RW rollback version, 32-bit unsigned integer. + * TODO: Get the relative offset of + * __image_data_offset within our RW image to + * accommodate image asymmetry. + */ + .area_name = "RW_RBVER", + .area_offset = CONFIG_EC_WRITABLE_STORAGE_OFF - + FMAP_REGION_START + CONFIG_RW_STORAGE_OFF + + RELATIVE_RO((uint32_t)__image_data_offset) + + offsetof(struct image_data, rollback_version), + .area_size = sizeof( + current_image_data.rollback_version), + .area_flags = FMAP_AREA_STATIC, + }, +#endif #ifdef CONFIG_RWSIG_TYPE_RWSIG { /* RW image signature */ diff --git a/common/rollback.c b/common/rollback.c new file mode 100644 index 0000000000..dbeffaa051 --- /dev/null +++ b/common/rollback.c @@ -0,0 +1,179 @@ +/* Copyright 2017 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. + */ + +/* Rollback protection logic. */ + +#include "common.h" +#include "console.h" +#include "flash.h" +#include "rollback.h" +#include "system.h" +#include "util.h" + +/* Number of rollback regions */ +#define ROLLBACK_REGIONS 2 + +/* + * Note: Do not change this structure without also updating + * common/firmware_image.S .image.ROLLBACK section. + */ +struct rollback_data { + int32_t rollback_min_version; + uint32_t cookie; +}; + +/* We need at least 2 erasable blocks in the rollback region. */ +BUILD_ASSERT(CONFIG_ROLLBACK_SIZE >= ROLLBACK_REGIONS*CONFIG_FLASH_ERASE_SIZE); +BUILD_ASSERT(sizeof(struct rollback_data) <= CONFIG_FLASH_ERASE_SIZE); + +static uintptr_t get_rollback_offset(int region) +{ + return CONFIG_ROLLBACK_OFF + region * CONFIG_FLASH_ERASE_SIZE; +} + +static int read_rollback(int region, struct rollback_data *data) +{ + uintptr_t offset; + + offset = get_rollback_offset(region); + + if (flash_read(offset, sizeof(*data), (char *)data)) + return EC_ERROR_UNKNOWN; + + return EC_SUCCESS; +} + +/* + * Get the most recent rollback information. + * + * @rollback_min_version: Minimum version to accept for rollback protection, + * or 0 if no rollback information is present. + * + * Return most recent region index on success (>= 0, or 0 if no rollback + * region is valid), negative value on error. + */ +static int get_latest_rollback(int32_t *rollback_min_version) +{ + int region; + int min_region = 0; + + *rollback_min_version = 0; + + for (region = 0; region < ROLLBACK_REGIONS; region++) { + struct rollback_data data; + + if (read_rollback(region, &data)) + return -1; + + /* Check if not initialized or invalid cookie. */ + if (data.cookie != CROS_EC_ROLLBACK_COOKIE) + continue; + + if (data.rollback_min_version > *rollback_min_version) { + min_region = region; + *rollback_min_version = data.rollback_min_version; + } + } + + return min_region; +} + +int32_t rollback_get_minimum_version(void) +{ + int32_t rollback_min_version; + + if (get_latest_rollback(&rollback_min_version) < 0) + return -1; + + return rollback_min_version; +} + +int rollback_update(int32_t next_min_version) +{ + struct rollback_data data; + uintptr_t offset; + int region; + int32_t current_min_version; + int ret; + + region = get_latest_rollback(¤t_min_version); + + if (region < 0) + return EC_ERROR_UNKNOWN; + + /* Do not accept to decrement the value. */ + if (next_min_version < current_min_version) + return EC_ERROR_INVAL; + + /* No need to update if version is already correct. */ + if (next_min_version == current_min_version) + return EC_SUCCESS; + + /* Use the other region. */ + region = (region + 1) % ROLLBACK_REGIONS; + + offset = get_rollback_offset(region); + + data.rollback_min_version = next_min_version; + data.cookie = CROS_EC_ROLLBACK_COOKIE; + + if (system_unsafe_to_overwrite(offset, CONFIG_FLASH_ERASE_SIZE)) + return EC_ERROR_ACCESS_DENIED; + + ret = flash_erase(offset, CONFIG_FLASH_ERASE_SIZE); + if (ret) + return ret; + + return flash_write(offset, sizeof(data), (char *)&data); +} + +static int command_rollback_info(int argc, char **argv) +{ + int region, ret, min_region; + int32_t rollback_min_version; + + min_region = get_latest_rollback(&rollback_min_version); + + if (min_region < 0) + return EC_ERROR_UNKNOWN; + + ccprintf("rollback minimum version: %d\n", rollback_min_version); + + for (region = 0; region < ROLLBACK_REGIONS; region++) { + struct rollback_data data; + + ret = read_rollback(region, &data); + if (ret) + return ret; + + ccprintf("rollback %d: %08x %08x%s\n", + region, data.rollback_min_version, data.cookie, + min_region == region ? " *" : ""); + } + + return EC_SUCCESS; +} +DECLARE_SAFE_CONSOLE_COMMAND(rollbackinfo, command_rollback_info, + NULL, + "Print rollback info"); + +static int command_rollback_update(int argc, char **argv) +{ + int32_t min_version; + char *e; + + if (argc < 2) + return EC_ERROR_PARAM_COUNT; + + min_version = strtoi(argv[1], &e, 0); + + if (*e || min_version < 0) + return EC_ERROR_PARAM1; + + return rollback_update(min_version); +} +DECLARE_CONSOLE_COMMAND(rollbackupdate, command_rollback_update, + "min_version", + "Update rollback info"); diff --git a/common/rwsig.c b/common/rwsig.c index 1ad3ed37f9..f7d9429270 100644 --- a/common/rwsig.c +++ b/common/rwsig.c @@ -9,6 +9,7 @@ #include "console.h" #include "ec_commands.h" +#include "rollback.h" #include "rsa.h" #include "sha256.h" #include "shared_mem.h" @@ -16,6 +17,7 @@ #include "usb_pd.h" #include "util.h" #include "vb21_struct.h" +#include "version.h" /* Console output macros */ #define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args) @@ -63,6 +65,10 @@ void check_rw_signature(void) const struct vb21_packed_key *vb21_key; const struct vb21_signature *vb21_sig; #endif +#ifdef CONFIG_ROLLBACK + int32_t rw_rollback_version; + int32_t min_rollback_version; +#endif /* Only the Read-Only firmware needs to do the signature check */ if (system_get_image_copy() != SYSTEM_IMAGE_RO) @@ -74,6 +80,18 @@ void check_rw_signature(void) CPRINTS("Verifying RW image..."); +#ifdef CONFIG_ROLLBACK + rw_rollback_version = system_get_rollback_version(SYSTEM_IMAGE_RW); + min_rollback_version = rollback_get_minimum_version(); + + if (rw_rollback_version < 0 || min_rollback_version < 0 || + rw_rollback_version < min_rollback_version) { + CPRINTS("Rollback error (%d < %d)", + rw_rollback_version, min_rollback_version); + return; + } +#endif + /* Large buffer for RSA computation : could be re-use afterwards... */ res = shared_mem_acquire(3 * RSANUMBYTES, (char **)&rsa_workbuf); if (res) { diff --git a/common/system.c b/common/system.c index e152696941..2ddcd570c2 100644 --- a/common/system.c +++ b/common/system.c @@ -617,6 +617,15 @@ const char *system_get_version(enum system_image_copy_t copy) return data ? data->version : ""; } +#ifdef CONFIG_ROLLBACK +int32_t system_get_rollback_version(enum system_image_copy_t copy) +{ + const struct image_data *data = system_get_image_data(copy); + + return data ? data->rollback_version : -1; +} +#endif + int system_get_image_used(enum system_image_copy_t copy) { const struct image_data *data = system_get_image_data(copy); diff --git a/common/version.c b/common/version.c index ff8207bf2c..06cbfbab01 100644 --- a/common/version.c +++ b/common/version.c @@ -7,9 +7,13 @@ #include <stdint.h> #include "common.h" +#include "compile_time_macros.h" #include "ec_version.h" #include "version.h" +BUILD_ASSERT(CONFIG_ROLLBACK_VERSION >= 0); +BUILD_ASSERT(CONFIG_ROLLBACK_VERSION <= INT32_MAX); + const struct image_data __keep current_image_data __attribute__((section(".rodata.ver"))) = { .cookie1 = CROS_EC_IMAGE_DATA_COOKIE1, @@ -17,6 +21,7 @@ const struct image_data __keep current_image_data #ifndef TEST_BUILD .size = (const uintptr_t)&__image_size, #endif + .rollback_version = CONFIG_ROLLBACK_VERSION, .cookie2 = CROS_EC_IMAGE_DATA_COOKIE2, }; diff --git a/include/config.h b/include/config.h index d44a5f7cc0..d3c661e52f 100644 --- a/include/config.h +++ b/include/config.h @@ -1132,6 +1132,17 @@ #undef CONFIG_ROLLBACK_SIZE /* + * Current rollback version. Meaningless for RO (but provides the minimum value + * that will be written to the rollback protection at flash time). + * + * For RW, rollback version included in version structure, used by RO to + * determine if the RW image is recent enough and can be jumped to. + * + * Valid values are >= 0, <= INT32_MAX (positive, 32-bit signed integer). + */ +#define CONFIG_ROLLBACK_VERSION 0 + +/* * Board Image ec.bin contains a RO firmware. If not defined, the image will * only contain the RW firmware. The RO firmware comes from another board. */ diff --git a/include/rollback.h b/include/rollback.h new file mode 100644 index 0000000000..7221691c1f --- /dev/null +++ b/include/rollback.h @@ -0,0 +1,32 @@ +/* Copyright 2017 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 __CROS_EC_ROLLBACK_H +#define __CROS_EC_ROLLBACK_H + +#define CROS_EC_ROLLBACK_COOKIE 0x0b112233 + +#ifndef __ASSEMBLER__ + +/** + * Get minimum version set by rollback protection blocks. + * + * @return Minimum rollback version, 0 if neither block is initialized, + * negative value on error. + */ +int rollback_get_minimum_version(void); + +/** + * Update rollback protection block to the version passed as parameter. + * + * @param next_min_version Minimum version to write in rollback block. + * + * @return EC_SUCCESS on success, EC_ERROR_* on error. + */ +int rollback_update(int32_t next_min_version); + +#endif + +#endif /* __CROS_EC_ROLLBACK_H */ diff --git a/include/system.h b/include/system.h index 38132140b5..32a544db49 100644 --- a/include/system.h +++ b/include/system.h @@ -194,6 +194,15 @@ int system_get_image_used(enum system_image_copy_t copy); int system_run_image_copy(enum system_image_copy_t copy); /** + * Get the rollback version for an image + * + * @param copy Image copy to get version from, or SYSTEM_IMAGE_UNKNOWN + * to get the version for the currently running image. + * @return The rollback version, negative value on error. + */ +int32_t system_get_rollback_version(enum system_image_copy_t copy); + +/** * Get the version string for an image * * @param copy Image copy to get version from, or SYSTEM_IMAGE_UNKNOWN diff --git a/include/version.h b/include/version.h index 94e8dd9ed5..14aa61a0e2 100644 --- a/include/version.h +++ b/include/version.h @@ -11,12 +11,13 @@ #include "common.h" #define CROS_EC_IMAGE_DATA_COOKIE1 0xce778899 -#define CROS_EC_IMAGE_DATA_COOKIE2 0xceaabbcc +#define CROS_EC_IMAGE_DATA_COOKIE2 0xceaabbdd struct image_data { uint32_t cookie1; char version[32]; uint32_t size; + int32_t rollback_version; uint32_t cookie2; } __packed; |