summaryrefslogtreecommitdiff
path: root/chip
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2016-07-23 17:02:18 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-07-26 17:32:02 -0700
commit7a9ba2bae9a7d1f23a0d70ab2ec299dc6f461e17 (patch)
tree3063a704460b4ee436b8ae06b5262769724a40b5 /chip
parentb3b92f45fdccd28c117a17bd4450d23a55d72210 (diff)
downloadchrome-ec-7a9ba2bae9a7d1f23a0d70ab2ec299dc6f461e17.tar.gz
cr50: process retry counter and act on it
A recent cr50 loader modification introduced a counter in a scratch register which is incremented on every startup. The idea is that valid RW would decrement the counter, signaling that the start was successful. Should the counter exceed the value of 5, the loader assumes that the RW being started is not fit to run, and picks the older RW to run, if available. This patch adds a function to process the startup retry counter. First of all the counter is zeroed, as this function is supposed to be called only once the RW run is considered successful and reliable. Then the current situation is examined. If the counter value read from the scratch register exceeds 5 AND running image is not the newer of A and B, it is considered an indication of a fallback from a bad newer image. To prevent the newer image from being considered a contender on the following startups, its header is corrupted. BRANCH=none BUG=chrome-os-partner:55151, chrome-os-partner:55667 TEST=modified code for testing purposes, by adding a call to system_process_retry_counter() to tpm_task() after line 534, which would cause the new function to be called soon after boot. built a new image and installed it on the debug board. Then modified the image to throw an exception early in the boot up sequence, and installed it as a newer image on the debug board. Observed the debug board restart the new image several time and then fall back to the older image, printing the following on the console: system_process_retry_counter:retry counter 7 corrupt_other_header: RW fallback must have happened, magic at 44000 before: ffffffff corrupt_other_header: magic after: 0 The following restarts start the older image without trying to run the failing newer image. Change-Id: Ia7497401e38fe2c3957af910cf745e45da985245 Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/362776 Reviewed-by: Randall Spangler <rspangler@chromium.org>
Diffstat (limited to 'chip')
-rw-r--r--chip/g/system.c110
1 files changed, 110 insertions, 0 deletions
diff --git a/chip/g/system.c b/chip/g/system.c
index 2bbd5d0301..7210875620 100644
--- a/chip/g/system.c
+++ b/chip/g/system.c
@@ -4,7 +4,10 @@
*/
#include "config.h"
+#include "console.h"
#include "cpu.h"
+#include "cpu.h"
+#include "flash.h"
#include "printf.h"
#include "registers.h"
#include "signed_header.h"
@@ -235,3 +238,110 @@ const char *system_get_version(enum system_image_copy_t copy)
return "Error";
}
+
+#ifdef BOARD_CR50
+/*
+ * Check wich of the two cr50 RW images is newer, return true if the first
+ * image is no older than the second one.
+ *
+ * Note that RO and RW images use the same header structure. When deciding
+ * which image to run, the boot ROM ignores the timestamp, but the cros loader
+ * considers the timestamp if all other fields are equal.
+ */
+static int a_is_newer_than_b(const struct SignedHeader *a,
+ const struct SignedHeader *b)
+{
+ if (a->epoch_ != b->epoch_)
+ return a->epoch_ > b->epoch_;
+ if (a->major_ != b->major_)
+ return a->major_ > b->major_;
+ if (a->minor_ != b->minor_)
+ return a->minor_ > b->minor_;
+
+ /* This comparison is not made by ROM. */
+ if (a->timestamp_ != b->timestamp_)
+ return a->timestamp_ > b->timestamp_;
+
+ return 1; /* All else being equal, consider A to be newer. */
+}
+
+/*
+ * Corrupt the 'magic' field of the passed in header. This prevents the
+ * 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)
+{
+ int rv;
+ const char zero[4] = {}; /* value to write to magic. */
+
+ /* Enable RW access to the other header. */
+ GREG32(GLOBALSEC, FLASH_REGION6_BASE_ADDR) = (uint32_t) header;
+ GREG32(GLOBALSEC, FLASH_REGION6_SIZE) = 1023;
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, EN, 1);
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, RD_EN, 1);
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, WR_EN, 1);
+
+ ccprintf("%s: RW fallback must have happened, magic at %p before: %x\n",
+ __func__, &header->magic, header->magic);
+
+ rv = flash_physical_write((intptr_t)&header->magic -
+ CONFIG_PROGRAM_MEMORY_BASE,
+ sizeof(zero), zero);
+
+ /* Disable W access to the other header. */
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, WR_EN, 0);
+ ccprintf("%s: magic after: %x\n",
+ __func__, header->magic);
+
+ return rv;
+}
+
+/*
+ * Value of the retry counter which, if exceeded, indicates that the currently
+ * running RW image is not well and is rebooting before bringing the system
+ * manages to come up.
+ */
+#define RW_BOOT_MAX_RETRY_COUNT 5
+
+int system_process_retry_counter(void)
+{
+ unsigned retry_counter;
+ struct SignedHeader *me, *other;
+
+ retry_counter = GREG32(PMU, LONG_LIFE_SCRATCH0);
+ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG0, 1);
+ GREG32(PMU, LONG_LIFE_SCRATCH0) = 0;
+ GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG0, 0);
+
+ if (retry_counter <= RW_BOOT_MAX_RETRY_COUNT)
+ return EC_SUCCESS;
+
+ ccprintf("%s:retry counter %d\n", __func__, retry_counter);
+
+ if (system_get_image_copy() == SYSTEM_IMAGE_RW) {
+ me = (struct SignedHeader *)
+ get_program_memory_addr(SYSTEM_IMAGE_RW);
+ other = (struct SignedHeader *)
+ get_program_memory_addr(SYSTEM_IMAGE_RW_B);
+ } else {
+ me = (struct SignedHeader *)
+ get_program_memory_addr(SYSTEM_IMAGE_RW_B);
+ other = (struct SignedHeader *)
+ get_program_memory_addr(SYSTEM_IMAGE_RW);
+ }
+
+ if (a_is_newer_than_b(me, other)) {
+ 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.
+ */
+ return corrupt_other_header(other);
+}
+#endif
+