summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/build.mk1
-rw-r--r--common/firmware_image.S8
-rw-r--r--common/firmware_image.lds.S6
-rw-r--r--common/fmap.c32
-rw-r--r--common/rollback.c179
-rw-r--r--common/rwsig.c18
-rw-r--r--common/system.c9
-rw-r--r--common/version.c5
-rw-r--r--include/config.h11
-rw-r--r--include/rollback.h32
-rw-r--r--include/system.h9
-rw-r--r--include/version.h3
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(&current_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;