summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2015-11-05 17:50:23 -0800
committerchrome-bot <chrome-bot@chromium.org>2015-11-10 06:54:43 -0800
commit725bef1b3b04761e0b385ba6757d0f8b100bebcf (patch)
tree6e9d160d8830527cde50695ffdd0c27cd7c9b154
parent4611f0fbc2a11530d33cfda4bb66c965d2344ea9 (diff)
downloadchrome-ec-725bef1b3b04761e0b385ba6757d0f8b100bebcf.tar.gz
cr50: RO Loader implementation
This code is a port of the sample loader application included in the FPGA update. Only the pieces relevant to straight verification and boot were ported. The loader generates a hash, inputs to which are the image body, state of fuses and state of flash INFO region, and the output is the value, which will unlock the region for execution, if it is correct. Only one image load is attempted, the image is supposed to be located in the flash at the offset of CONFIG_RW_MEM_OFF. BRANCH=none BUG=chrome-os-partner:43025 TEST=with the rest of the patches applied the RO image successfully verifies and starts up the RW image. Change-Id: I26e1fbdaeb8b23d519c1a328526a3422231bb322 Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/311316 Reviewed-by: Bill Richardson <wfrichar@chromium.org>
-rw-r--r--chip/g/build.mk11
-rw-r--r--chip/g/loader/debug_printf.h6
-rw-r--r--chip/g/loader/hw_sha256.c110
-rw-r--r--chip/g/loader/hw_sha256.h27
-rw-r--r--chip/g/loader/launch.c229
-rw-r--r--chip/g/loader/main.c18
-rw-r--r--chip/g/loader/rom_flash.c114
-rw-r--r--chip/g/loader/rom_flash.h59
-rw-r--r--chip/g/loader/setup.c54
-rw-r--r--chip/g/loader/setup.h18
-rw-r--r--chip/g/loader/verify.c201
-rw-r--r--chip/g/loader/verify.h16
12 files changed, 858 insertions, 5 deletions
diff --git a/chip/g/build.mk b/chip/g/build.mk
index 2a6910b82b..4e9ec7208f 100644
--- a/chip/g/build.mk
+++ b/chip/g/build.mk
@@ -39,8 +39,15 @@ chip-$(CONFIG_USB_BLOB)+=usb_blob.o
chip-$(CONFIG_FLASH)+=flash.o
ifneq ($(CONFIG_CUSTOMIZED_RO),)
-custom-ro_objs-y = chip/g/loader/main.o
-custom-ro_objs-y += chip/g/system.o chip/g/uart.o
+custom-ro_objs-y = chip/g/clock.o
+custom-ro_objs-y += chip/g/loader/hw_sha256.o
+custom-ro_objs-y += chip/g/loader/launch.o
+custom-ro_objs-y += chip/g/loader/main.o
+custom-ro_objs-y += chip/g/loader/rom_flash.o
+custom-ro_objs-y += chip/g/loader/setup.o
+custom-ro_objs-y += chip/g/loader/verify.o
+custom-ro_objs-y += chip/g/system.o
+custom-ro_objs-y += chip/g/uart.o
custom-ro_objs-y += common/printf.o
custom-ro_objs-y += common/util.o
custom-ro_objs-y += core/cortex-m/init.o
diff --git a/chip/g/loader/debug_printf.h b/chip/g/loader/debug_printf.h
index ff31e005a9..73e04a9e0c 100644
--- a/chip/g/loader/debug_printf.h
+++ b/chip/g/loader/debug_printf.h
@@ -7,4 +7,10 @@
void debug_printf(const char *format, ...);
+#ifdef DEBUG
+#define VERBOSE debug_printf
+#else
+#define VERBOSE(...)
+#endif
+
#endif /* __EC_CHIP_G_LOADER_DEBUG_PRINTF_H */
diff --git a/chip/g/loader/hw_sha256.c b/chip/g/loader/hw_sha256.c
new file mode 100644
index 0000000000..af6ce8de5c
--- /dev/null
+++ b/chip/g/loader/hw_sha256.c
@@ -0,0 +1,110 @@
+/* Copyright 2015 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.
+ */
+
+#include "debug_printf.h"
+#include "registers.h"
+#include "setup.h"
+
+static void _sha_write(const void *data, size_t n)
+{
+ const uint8_t *bp = (const uint8_t *)data;
+ const uint32_t *wp;
+
+ while (n && ((uint32_t)bp & 3)) { /* Feed unaligned start bytes. */
+ *((uint8_t *)GREG32_ADDR(KEYMGR, SHA_INPUT_FIFO)) = *bp++;
+ n -= 1;
+ }
+
+ wp = (uint32_t *)bp;
+ while (n >= 32) { /* Feed groups of aligned words. */
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = *wp++;
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = *wp++;
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = *wp++;
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = *wp++;
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = *wp++;
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = *wp++;
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = *wp++;
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = *wp++;
+ n -= 32;
+ }
+
+ while (n >= 4) { /* Feed individual aligned words. */
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = *wp++;
+ n -= 4;
+ }
+
+ bp = (uint8_t *)wp;
+ while (n) { /* Feed remaing bytes. */
+ *((uint8_t *)GREG32_ADDR(KEYMGR, SHA_INPUT_FIFO)) = *bp++;
+ n -= 1;
+ }
+}
+
+static void _sha_wait(uint32_t *digest)
+{
+ int i;
+
+ /*
+ * Wait for result. TODO: what harm does glitching do? Read out
+ * non-digest? Old digest?
+ */
+ while (!GREG32(KEYMGR, SHA_ITOP))
+ ;
+
+ /* Read out final digest. */
+ for (i = 0; i < 8; ++i)
+ *digest++ = GREG32_ADDR(KEYMGR, SHA_STS_H0)[i];
+
+ GREG32(KEYMGR, SHA_ITOP) = 0; /* Clear status. */
+}
+
+void hwSHA256(const void *data, size_t n, uint32_t *digest)
+{
+ GREG32(KEYMGR, SHA_ITOP) = 0; /* Clear status. */
+
+ GREG32(KEYMGR, SHA_CFG_MSGLEN_LO) = n;
+ GREG32(KEYMGR, SHA_CFG_MSGLEN_HI) = 0;
+
+ GWRITE_FIELD(KEYMGR, SHA_CFG_EN, INT_EN_DONE, 1);
+ GWRITE_FIELD(KEYMGR, SHA_TRIG, TRIG_GO, 1);
+
+ _sha_write(data, n);
+ _sha_wait(digest);
+}
+
+void hwKeyLadderStep(uint32_t cert, const uint32_t *input)
+{
+ uint32_t flags;
+
+ GREG32(KEYMGR, SHA_ITOP) = 0; /* clear status. */
+
+ VERBOSE("Cert %2u: ", cert);
+
+ GWRITE_FIELD(KEYMGR, SHA_USE_CERT, INDEX, cert);
+ GWRITE_FIELD(KEYMGR, SHA_USE_CERT, ENABLE, 1);
+ GWRITE_FIELD(KEYMGR, SHA_CFG_EN, INT_EN_DONE, 1);
+ GWRITE_FIELD(KEYMGR, SHA_TRIG, TRIG_GO, 1);
+
+ if (input) {
+ int i;
+
+ for (i = 0; i < 8; ++i)
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = *input++;
+
+ GWRITE_FIELD(KEYMGR, SHA_TRIG, TRIG_STOP, 1);
+ }
+
+ while (!GREG32(KEYMGR, SHA_ITOP))
+ ;
+
+ GREG32(KEYMGR, SHA_ITOP) = 0; /* Clear status. */
+
+ flags = GREG32(KEYMGR, HKEY_ERR_FLAGS);
+ if (flags)
+ debug_printf("Cert %2u: fail %x\n", cert, flags);
+ else
+ VERBOSE("flags %x\n", flags);
+}
+
diff --git a/chip/g/loader/hw_sha256.h b/chip/g/loader/hw_sha256.h
new file mode 100644
index 0000000000..0940efae40
--- /dev/null
+++ b/chip/g/loader/hw_sha256.h
@@ -0,0 +1,27 @@
+/* Copyright 2015 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_CHIP_G_LOADER_HW_SHA256_H
+#define __EC_CHIP_G_LOADER_HW_SHA256_H
+
+#include <inttypes.h>
+#include <stddef.h>
+
+#define SHA256_DIGEST_BYTES 32
+#define SHA256_DIGEST_WORDS (SHA256_DIGEST_BYTES / sizeof(uint32_t))
+
+typedef struct {
+ uint32_t digest[SHA256_DIGEST_WORDS];
+} hwSHA256_CTX;
+
+void hwSHA256_init(hwSHA256_CTX *ctx);
+void hwSHA256_update(hwSHA256_CTX *ctx, const void *data, size_t len);
+const uint8_t *hwSHA256_final(hwSHA256_CTX *ctx);
+
+void hwSHA256(const void *data, size_t len, uint32_t *digest);
+
+void hwKeyLadderStep(uint32_t certificate, const uint32_t *input);
+
+#endif /* __EC_CHIP_G_LOADER_HW_SHA256_H */
diff --git a/chip/g/loader/launch.c b/chip/g/loader/launch.c
new file mode 100644
index 0000000000..54d75e8dd1
--- /dev/null
+++ b/chip/g/loader/launch.c
@@ -0,0 +1,229 @@
+/* Copyright 2015 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.
+ */
+
+#include "debug_printf.h"
+#include "hw_sha256.h"
+#include "registers.h"
+#include "rom_flash.h"
+#include "setup.h"
+#include "verify.h"
+#include "uart.h"
+
+#include "util/signer/common/signed_header.h"
+
+static int unlockedForExecution(void)
+{
+ return GREAD_FIELD(GLOBALSEC, SB_COMP_STATUS, SB_BL_SIG_MATCH);
+}
+
+void _jump_to_address(const void *addr)
+{
+ REG32(GC_M3_VTOR_ADDR) = (unsigned)addr; /* Set vector base. */
+
+ __asm__ volatile("ldr sp, [%0]; \
+ ldr pc, [%0, #4];"
+ : : "r"(addr)
+ : "memory");
+
+ __builtin_unreachable();
+}
+
+void tryLaunch(uint32_t adr, size_t max_size)
+{
+ static struct {
+ uint32_t img_hash[SHA256_DIGEST_WORDS];
+ uint32_t fuses_hash[SHA256_DIGEST_WORDS];
+ uint32_t info_hash[SHA256_DIGEST_WORDS];
+ } hashes;
+ static uint32_t hash[SHA256_DIGEST_WORDS];
+ static uint32_t fuses[FUSE_MAX];
+ static uint32_t info[INFO_MAX];
+ int i;
+ uint32_t major;
+ const uint32_t FAKE_rom_hash[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+ const SignedHeader *hdr = (const SignedHeader *)(adr);
+
+ memset(&hashes, 0, sizeof(hashes));
+
+ /* Sanity check image header. */
+ if (hdr->magic != -1)
+ return;
+ if (hdr->image_size > max_size)
+ return;
+
+ /* Sanity checks that image belongs at adr. */
+ if (hdr->ro_base < adr)
+ return;
+ if (hdr->ro_max > adr + max_size)
+ return;
+ if (hdr->rx_base < adr)
+ return;
+ if (hdr->rx_max > adr + max_size)
+ return;
+
+ VERBOSE("considering image at 0x%8x\n", hdr);
+ VERBOSE("image size 0x%8x\n", hdr->image_size);
+ VERBOSE("hashing from 0x%8x to 0x%8x\n",
+ &hdr->tag, adr + hdr->image_size);
+
+ /* Setup candidate execution region 1 based on header information. */
+ /* TODO: harden against glitching: multi readback, check? */
+ GREG32(GLOBALSEC, CPU0_I_STAGING_REGION1_BASE_ADDR) = hdr->rx_base;
+ GREG32(GLOBALSEC, CPU0_I_STAGING_REGION1_SIZE) =
+ hdr->rx_max - hdr->rx_base - 1;
+ GWRITE_FIELD(GLOBALSEC, CPU0_I_STAGING_REGION1_CTRL, EN, 1);
+ GWRITE_FIELD(GLOBALSEC, CPU0_I_STAGING_REGION1_CTRL, RD_EN, 1);
+ hwSHA256(&hdr->tag,
+ hdr->image_size - offsetof(SignedHeader, tag),
+ hashes.img_hash);
+
+ VERBOSE("img_hash : %.32h\n", hashes.img_hash);
+
+ /* Sense fuses into RAM array; hash array. */
+ /* TODO: is this glitch resistant enough? Certainly is simple.. */
+ for (i = 0; i < FUSE_MAX; ++i)
+ fuses[i] = FUSE_IGNORE;
+
+ for (i = 0; i < FUSE_MAX; ++i) {
+ /*
+ * For the fuses the header cares about, read their values
+ * into the map.
+ */
+ if (hdr->fusemap[i>>5] & (1 << (i&31))) {
+ /*
+ * BNK0_INTG_CHKSUM is the first fuse and as such the
+ * best reference to the base address of the fuse
+ * memory map.
+ */
+ fuses[i] = GREG32_ADDR(FUSE, BNK0_INTG_CHKSUM)[i];
+ }
+ }
+
+ hwSHA256(fuses, sizeof(fuses), hashes.fuses_hash);
+
+ VERBOSE("fuses_hash: %.32h\n", hashes.fuses_hash);
+
+ /* Sense info into RAM array; hash array. */
+ for (i = 0; i < INFO_MAX; ++i)
+ info[i] = INFO_IGNORE;
+
+ for (i = 0; i < INFO_MAX; ++i) {
+ if (hdr->infomap[i>>5] & (1 << (i&31))) {
+ uint32_t val = 0;
+ /* read 2nd bank of info */
+ int retval = flash_info_read(i + INFO_MAX, &val);
+
+ info[i] ^= val ^ retval;
+ }
+ }
+
+ hwSHA256(info, sizeof(info), hashes.info_hash);
+ VERBOSE("info_hash : %.32h\n", hashes.info_hash);
+
+ /* Hash our set of hashes to get final hash. */
+ hwSHA256(&hashes, sizeof(hashes), hash);
+
+ /*
+ * Write measured hash to unlock register to try and unlock execution.
+ * This would match when doing warm-boot from suspend, so we can avoid
+ * the slow RSA verify.
+ */
+ for (i = 0; i < SHA256_DIGEST_WORDS; ++i)
+ GREG32_ADDR(GLOBALSEC, SB_BL_SIG0)[i] = hash[i];
+
+ /*
+ * Unlock attempt. Value written is irrelevant, as long as something
+ * is written.
+ */
+ GREG32(GLOBALSEC, SIG_UNLOCK) = 1;
+
+ if (!unlockedForExecution()) {
+ /* Assume warm-boot failed; do full RSA verify. */
+ LOADERKEY_verify(hdr->keyid, hdr->signature, hash);
+ /*
+ * PWRDN_SCRATCH* should be write-locked, tied to successful
+ * SIG_MATCH. Thus ARM is only able to write this hash if
+ * signature was correct.
+ */
+ for (i = 0; i < SHA256_DIGEST_WORDS; ++i)
+ /* TODO: verify written values as glitch protection? */
+ GREG32_ADDR(PMU, PWRDN_SCRATCH8)[i] = hash[i];
+ }
+
+
+ if (!unlockedForExecution()) {
+ debug_printf("Failed to unlock for execution image at 0x%08x\n",
+ adr);
+ return;
+ }
+
+ /*
+ * Write PMU_PWRDN_SCRATCH_LOCK1_OFFSET to lock against rewrites.
+ * TODO: glitch resist
+ */
+ GREG32(PMU, PWRDN_SCRATCH_LOCK1) = 1;
+
+ /*
+ * Drop software level to stop SIG_MATCH from future write-unlocks.
+ * TODO: glitch detect / verify?
+ */
+ GREG32(GLOBALSEC, SOFTWARE_LVL) = 0x33;
+
+ /* Write hdr->tag, hdr->epoch_ to KDF engine FWR[0..7] */
+ for (i = 0; i < ARRAY_SIZE(hdr->tag); ++i)
+ GREG32_ADDR(KEYMGR, HKEY_FWR0)[i] = hdr->tag[i];
+
+ GREG32(KEYMGR, HKEY_FWR7) = hdr->epoch_;
+
+ /* Crank keyladder */
+ if (!(GREAD(FUSE, FLASH_PERSO_PAGE_LOCK) &
+ (GC_FUSE_HIK_CREATE_LOCK_VAL_MASK <<
+ GC_FUSE_HIK_CREATE_LOCK_VAL_LSB))) {
+ VERBOSE("Re-reading INFO0\n");
+ /*
+ * Needed because FUSE_FLASH_PERSO_PAGE_LOCK_OFFSET isn't
+ * blown) wipe out the flash secrets saved in keymgr and
+ * re-read info0
+ */
+ GREG32(KEYMGR, FLASH_RCV_WIPE) = 1;
+ GREG32(FLASH, FSH_ENABLE_INFO0_SHADOW_READ) = 1;
+ }
+
+ /* Turn up random stalls for SHA */
+ GREG32(KEYMGR, SHA_RAND_STALL_CTL_FREQ) = 0; /* 0:50% */
+
+ major = hdr->major_;
+ GREG32(KEYMGR, FW_MAJOR_VERSION) = major;
+
+ /*
+ * Lock FWR (NOTE: needs to happen after writing major!) TODO: glitch
+ * protect?
+ */
+ GREG32(KEYMGR, FWR_VLD) = 2;
+ GREG32(KEYMGR, FWR_LOCK) = 1;
+
+ hwKeyLadderStep(40, FAKE_rom_hash);
+
+ /* TODO: do cert #40 and lock in ROM? */
+ GREG32(GLOBALSEC, HIDE_ROM) = 1;
+
+ /* TODO: bump runlevel(s) according to signature header */
+ /*
+ * Flash write protect entire image area (to guard signed blob)
+ * REGION0 protects boot_loader, use REGION1 to protect app
+ */
+ GREG32(GLOBALSEC, FLASH_REGION1_BASE_ADDR) = adr;
+ GREG32(GLOBALSEC, FLASH_REGION1_SIZE) = hdr->image_size - 1;
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION1_CTRL, EN, 1);
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION1_CTRL, RD_EN, 1);
+
+ /* TODO: lock FLASH_REGION 1? */
+ disarmRAMGuards();
+
+ debug_printf("Valid image found at 0x%08x, jumping\n", hdr);
+ uart_tx_flush();
+
+ _jump_to_address(&hdr[1]);
+}
diff --git a/chip/g/loader/main.c b/chip/g/loader/main.c
index 6e913dcf6d..c72b063710 100644
--- a/chip/g/loader/main.c
+++ b/chip/g/loader/main.c
@@ -6,7 +6,10 @@
#include "common.h"
#include "debug_printf.h"
#include "printf.h"
+#include "registers.h"
+#include "setup.h"
#include "system.h"
+#include "trng.h"
#include "uart.h"
/*
@@ -58,9 +61,18 @@ void panic_printf(const char *format, ...)
int main(void)
{
- debug_printf("Hello world\n");
- while (1)
- ;
+ init_trng();
+ uart_init();
+ debug_printf("\n\nCR50 RO, %8u_%u@%u\n", GREG32(SWDP, BUILD_DATE),
+ GREG32(SWDP, BUILD_TIME), GREG32(SWDP, P4_LAST_SYNC));
+ unlockFlashForRW();
+
+ /* Trying RW A only for now */
+ tryLaunch(CONFIG_PROGRAM_MEMORY_BASE + CONFIG_RW_MEM_OFF,
+ CONFIG_FLASH_SIZE/2 - CONFIG_RW_MEM_OFF);
+ debug_printf("No valid image found, not sure what to do...\n");
+ halt();
+ return 1;
}
void panic_reboot(void)
diff --git a/chip/g/loader/rom_flash.c b/chip/g/loader/rom_flash.c
new file mode 100644
index 0000000000..eec0bc4733
--- /dev/null
+++ b/chip/g/loader/rom_flash.c
@@ -0,0 +1,114 @@
+/* Copyright 2015 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.
+ */
+
+#include "debug_printf.h"
+#include "setup.h"
+#include "rom_flash.h"
+
+static int _flash_error(void)
+{
+ int retval = GREG32(FLASH, FSH_ERROR);
+
+ if (!retval)
+ return 0;
+
+ debug_printf("Register FLASH_FSH_ERROR is not zero (found %x).\n");
+ debug_printf("Will read again to verify FSH_ERROR was cleared ");
+ debug_printf("and then continue...\n", retval);
+
+ retval = GREG32(FLASH, FSH_ERROR);
+ if (retval) {
+ debug_printf("ERROR: Read to FLASH_FSH_ERROR (%x) ");
+ debug_printf("did not clear it\n", retval);
+ }
+
+ return retval;
+}
+
+/* Verify the flash controller is awake. */
+static int _check_flash_is_awake(void)
+{
+ int retval;
+
+ GREG32(FLASH, FSH_TRANS) = 0xFFFFFFFF;
+ retval = GREG32(FLASH, FSH_TRANS);
+ GREG32(FLASH, FSH_TRANS) = 0x0;
+
+ if (retval == 0) {
+ debug_printf("ERROR:FLASH Controller seems unresponsive. ");
+ debug_printf("Did you make sure to run 'reseth'?\n");
+ return E_FL_NOT_AWAKE;
+ }
+
+ return 0;
+}
+
+/* Send cmd to flash controller. */
+static int _flash_cmd(uint32_t fidx, uint32_t cmd)
+{
+ int cnt, retval;
+
+ /* Activate controller. */
+ GREG32(FLASH, FSH_PE_EN) = FSH_OP_ENABLE;
+ GREG32_ADDR(FLASH, FSH_PE_CONTROL0)[fidx] = cmd;
+
+ /* wait on FSH_PE_EN (means the operation started) */
+ cnt = 500; /* TODO(mschilder): pick sane value. */
+
+ do {
+ retval = GREG32(FLASH, FSH_PE_EN);
+ } while (retval && cnt--);
+
+ if (retval) {
+ debug_printf("ERROR: FLASH_FSH_PE_EN never went to 0, is ");
+ debug_printf("0x%x after timeout\n", retval);
+ return E_FL_TIMEOUT;
+ }
+
+ /*
+ * wait 100us before checking FSH_PE_CONTROL (means the operation
+ * ended)
+ */
+ cnt = 1000000;
+ do {
+ retval = GREG32_ADDR(FLASH, FSH_PE_CONTROL0)[fidx];
+ } while (retval && --cnt);
+
+ if (retval) {
+ debug_printf
+ ("ERROR: FLASH_FSH_PE_CONTROL%d is 0x%x after timeout\n",
+ fidx, retval);
+ GREG32_ADDR(FLASH, FSH_PE_CONTROL0)[fidx] = 0;
+ return E_FL_TIMEOUT;
+ }
+
+ return 0;
+}
+
+int flash_info_read(uint32_t offset, uint32_t *dst)
+{
+ int retval;
+
+ /* Make sure flash controller is awake. */
+ retval = _check_flash_is_awake();
+ if (retval)
+ return retval;
+
+ GWRITE_FIELD(FLASH, FSH_TRANS, OFFSET, offset);
+ GWRITE_FIELD(FLASH, FSH_TRANS, MAINB, 1);
+ GWRITE_FIELD(FLASH, FSH_TRANS, SIZE, 1);
+
+ retval = _flash_cmd(1, FSH_OP_READ);
+ if (retval)
+ return retval;
+
+ if (_flash_error())
+ return E_FL_ERROR;
+
+ if (!retval)
+ *dst = GREG32(FLASH, FSH_DOUT_VAL1);
+
+ return retval;
+}
diff --git a/chip/g/loader/rom_flash.h b/chip/g/loader/rom_flash.h
new file mode 100644
index 0000000000..7895d14af2
--- /dev/null
+++ b/chip/g/loader/rom_flash.h
@@ -0,0 +1,59 @@
+/* Copyright 2015 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_CHIP_G_LOADER_ROM_FLASH_H
+#define __EC_CHIP_G_LOADER_ROM_FLASH_H
+
+#include "registers.h"
+
+#define FSH_OP_BULKERASE GC_CONST_FSH_PE_CONTROL_BULKERASE
+#define FSH_OP_ENABLE GC_CONST_FSH_PE_EN
+#define FSH_OP_ERASE GC_CONST_FSH_PE_CONTROL_ERASE
+#define FSH_OP_PROGRAM GC_CONST_FSH_PE_CONTROL_PROGRAM
+#define FSH_OP_READ GC_CONST_FSH_PE_CONTROL_READ
+
+#if 0
+#define num_flashes 2
+/*
+ * FIX ME: words_per_row = m.get_flash_param("FlashWordsPerRow")
+ * FIX ME: rows_per_page = m.get_flash_param("FlashRowsPerPage")
+ * FIX ME: words_per_page = int(words_per_row)*int(rows_per_page)
+ */
+#define words_per_page 512
+/* This is BAD... This number is based on the TSMC spec Nmp=Tprog/Tsmp */
+#define _max_prog_cycles 9
+/* This is BAD... This number is based on the TSMC spec Nme=Terase/Tsme */
+#define _max_erase_cycles 45
+/* This is BAD... This number is based on the TSMC spec Nme=Terase/Tsme */
+#define _max_bulkerase_cycles 45
+#define _debug_flash 0
+
+/* write words to flash */
+int flash_write(uint32_t fidx, uint32_t offset,
+ const uint32_t *data, uint32_t size);
+
+/* erase one page */
+int flash_erase(uint32_t fidx, uint32_t page);
+
+/* erase entire bank */
+int flash_wipe(uint32_t fidx);
+#endif
+
+#define E_FL_NOT_AWAKE 1
+#define E_FL_TIMEOUT 2
+#define E_FL_BAD_MAINB 3
+#define E_FL_BAD_SIZE 4
+#define E_FL_BAD_PTR 5
+#define E_FL_BAD_BANK 6
+#define E_FL_WRITE_FAIL 7
+#define E_FL_ERASE_FAIL 8
+#define E_FL_WIPE_FAIL 9
+#define E_FL_ERROR 10
+
+/* read single word from info block */
+int flash_info_read(uint32_t offset, uint32_t *dst);
+
+
+#endif /* __EC_CHIP_G_LOADER_ROM_FLASH_H */
diff --git a/chip/g/loader/setup.c b/chip/g/loader/setup.c
new file mode 100644
index 0000000000..106cdecb15
--- /dev/null
+++ b/chip/g/loader/setup.c
@@ -0,0 +1,54 @@
+/* Copyright 2015 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.
+ */
+
+#include "debug_printf.h"
+#include "link_defs.h"
+#include "registers.h"
+#include "setup.h"
+
+/* Is there a system wide function for this? */
+void halt(void)
+{
+ while (1)
+ ;
+}
+
+void checkBuildVersion(void)
+{
+ uint32_t last_sync = GREG32(SWDP, P4_LAST_SYNC);
+
+ if (last_sync == GC_SWDP_P4_LAST_SYNC_DEFAULT)
+ return;
+
+ debug_printf("compiled for %u, not willing to run on %u\n",
+ GC_SWDP_P4_LAST_SYNC_DEFAULT, last_sync);
+ halt();
+}
+
+void unlockFlashForRW(void)
+{
+ uint32_t text_end = ((uint32_t)(&__ro_end) +
+ (uint32_t)(&__data_end) -
+ (uint32_t)(&__data_start) +
+ CONFIG_FLASH_BANK_SIZE)
+ & ~(CONFIG_FLASH_BANK_SIZE - 1);
+
+ GREG32(GLOBALSEC, FLASH_REGION1_BASE_ADDR) = text_end;
+ GREG32(GLOBALSEC, FLASH_REGION1_SIZE) =
+ CONFIG_FLASH_SIZE - text_end - 1;
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION1_CTRL, EN, 1);
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION1_CTRL, RD_EN, 1);
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION1_CTRL, WR_EN, 0);
+}
+
+void disarmRAMGuards(void)
+{
+ GWRITE_FIELD(GLOBALSEC, CPU0_D_REGION0_CTRL, EN, 1);
+ GWRITE_FIELD(GLOBALSEC, CPU0_D_REGION0_CTRL, RD_EN, 1);
+ GWRITE_FIELD(GLOBALSEC, CPU0_D_REGION0_CTRL, WR_EN, 1);
+ GWRITE_FIELD(GLOBALSEC, CPU0_D_REGION1_CTRL, EN, 1);
+ GWRITE_FIELD(GLOBALSEC, CPU0_D_REGION1_CTRL, RD_EN, 1);
+ GWRITE_FIELD(GLOBALSEC, CPU0_D_REGION1_CTRL, WR_EN, 1);
+}
diff --git a/chip/g/loader/setup.h b/chip/g/loader/setup.h
new file mode 100644
index 0000000000..4244696ef5
--- /dev/null
+++ b/chip/g/loader/setup.h
@@ -0,0 +1,18 @@
+/* Copyright 2015 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_CHIP_G_LOADER_SETUP_H
+#define __EC_CHIP_G_LOADER_SETUP_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+void checkBuildVersion(void);
+void disarmRAMGuards(void);
+void halt(void);
+void tryLaunch(uint32_t adr, size_t max_size);
+void unlockFlashForRW(void);
+
+#endif /* __EC_CHIP_G_LOADER_SETUP_H */
diff --git a/chip/g/loader/verify.c b/chip/g/loader/verify.c
new file mode 100644
index 0000000000..d852f16c9a
--- /dev/null
+++ b/chip/g/loader/verify.c
@@ -0,0 +1,201 @@
+/* Copyright 2015 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.
+ */
+#include "debug_printf.h"
+#include "hw_sha256.h"
+#include "registers.h"
+#include "setup.h"
+#include "trng.h"
+
+#define LOADERKEYEXP 3
+#define RSA_NUM_WORDS 96
+#define RSA_NUM_BYTES (RSA_NUM_WORDS * 4)
+
+
+static const uint32_t LOADERKEY_A[RSA_NUM_WORDS + 1] = {
+ 0xea54076f, 0xe986c871, 0x8cffffb4, 0xd7c50bda, 0x30700ee0, 0xc023a878,
+ 0x30e7fdf8, 0x5bb0c06f, 0x1d25d80f, 0x18e181f7, 0xfbf7a8b0, 0x331c16d4,
+ 0xeb042379, 0x4cef13ec, 0x5b2072e7, 0xc807b01d, 0x443fb117, 0xd2e04e5b,
+ 0xcb984393, 0x85d90d9d, 0x0332dcb8, 0xd42ccacf, 0x787e3947, 0x1975095c,
+ 0x2d523b0b, 0xf815be95, 0x00db9a2c, 0x6c08442b, 0x57a022bb, 0x9d5c84ed,
+ 0x46a6d275, 0x4392dcf8, 0xfa6812e3, 0xe0f3a3e6, 0xc8ff3f61, 0xd518dbac,
+ 0xbba7376a, 0x767a219a, 0x9d153119, 0x980b16f8, 0x79eb5078, 0xb869924d,
+ 0x2e392cc2, 0x76c04f32, 0xe35ea788, 0xcb67fa62, 0x30efec79, 0x36f04ae0,
+ 0x2212a5fc, 0x51c41de8, 0x2b0b84db, 0x6803ca1c, 0x39a248fd, 0xa0c31ee2,
+ 0xb1ca22b6, 0x16e54056, 0x086f6591, 0x3825208d, 0x079c157b, 0xe51c15a6,
+ 0x0dd1c66f, 0x8267b8ae, 0xf06b4f85, 0xc68b27ab, 0x31bcd5fc, 0x34d563b7,
+ 0xc4d2212e, 0x1e770199, 0xaf797061, 0x824d4853, 0x526e18cd, 0x4bb8a0dc,
+ 0xeb9377fe, 0x04fda73c, 0x2933f8a6, 0xe16c0432, 0x40ea1bd5, 0x9efcd77e,
+ 0x92be9e55, 0x003c1128, 0x48442cf9, 0x80b4fb31, 0xfe1e3df3, 0x1d28e14d,
+ 0xe99c0f9d, 0x521d38c2, 0x0082c4f1, 0xcff25d56, 0x0d3e7186, 0xe72b98f0,
+ 0xefaa5689, 0x74051ed5, 0x6b7e7fff, 0x822b1944, 0x77a94732, 0x8d0b9aaf,
+ 0x7a8ee958
+};
+
+const uint32_t LOADERKEY_B[RSA_NUM_WORDS + 1] = {
+ 0xeea8b39f, 0xdfa457a1, 0x8b81fdc3, 0xb0204c84, 0x297b9db2, 0xaa70318d,
+ 0x8cd41a68, 0x4aa0f9bb, 0xf63f9d69, 0xf0fe64b0, 0x96e42e2d, 0x5e494b1d,
+ 0x066cefd0, 0xde949c16, 0xc92499ed, 0x92229990, 0x48ac3b1a, 0x1dfc2388,
+ 0xda71d258, 0x826ddedf, 0xd0220e70, 0x6140dedf, 0x92bcdec7, 0xcdf91c22,
+ 0xaa110aed, 0xc371c2f9, 0xa3fedf2a, 0xfd2c6a07, 0xe71aabce, 0x7f426484,
+ 0x0ac51128, 0x4bab4ca2, 0x0162d0b9, 0x49fef7e3, 0xeda8664e, 0x14b92b7a,
+ 0x0397dbb7, 0x5b9eb94a, 0x069b5059, 0x3851f46b, 0x45bbcaba, 0x0b812652,
+ 0x7cd8b10b, 0xcaeccc32, 0x0ffd08e3, 0xfe6f0306, 0x8c02d5f7, 0xafdc4595,
+ 0xe0edda47, 0x0cc821db, 0x50beeae5, 0xb9868c18, 0xefd2de11, 0xdfecd15c,
+ 0xa8937a70, 0x223d9d95, 0x1b70848b, 0x54fa9176, 0x8bf012ef, 0xd37c1446,
+ 0xf9a7ebeb, 0xbf2dfa9a, 0xdc6b8ea0, 0xe5f8bc4d, 0x539222b5, 0x192521e4,
+ 0xe7088628, 0x2646bb56, 0x6fcc5d70, 0x3f1cd8e9, 0xae9cec24, 0xf53b6559,
+ 0x6f091891, 0x5342fa61, 0xbfee50e9, 0x211ad58a, 0xd1c5aa17, 0x252dfa56,
+ 0x17131164, 0x4630a459, 0x2f681f51, 0x3fb9ab3c, 0x6c8e0a70, 0xa34a868b,
+ 0xe960e702, 0xa470d241, 0x00647369, 0xa4c25391, 0xd1926cf9, 0x5fce5488,
+ 0xd171cb2e, 0x8a7c982e, 0xc89cbe39, 0xc0e019d8, 0x82cd1ebe, 0x68918fce,
+ 0x5ec138fd
+};
+
+#define RANDOM_STEP 5
+
+inline uint32_t bswap(uint32_t a)
+{
+ uint32_t result;
+
+ __asm__ volatile("rev %0, %1;" : "=r"(result) : "r"(a));
+
+ return result;
+}
+
+/* Montgomery c[] += a * b[] / R % key. */
+static void montMulAdd(const uint32_t *key,
+ uint32_t *c, const uint32_t a,
+ const uint32_t *b)
+{
+ register uint64_t tmp;
+ uint32_t i, A, B, d0;
+
+ {
+
+ tmp = c[0] + (uint64_t)a * b[0];
+ A = tmp >> 32;
+ d0 = (uint32_t)tmp * *key++;
+ tmp = (uint32_t)tmp + (uint64_t)d0 * *key++;
+ B = tmp >> 32;
+ }
+
+ for (i = 0; i < RSA_NUM_WORDS - 1; ++i) {
+ tmp = A + (uint64_t)a * b[i + 1] + c[i + 1];
+ A = tmp >> 32;
+ tmp = B + (uint64_t)d0 * *key++ + (uint32_t)tmp;
+ c[i] = (uint32_t)tmp;
+ B = tmp >> 32;
+ }
+
+ c[RSA_NUM_WORDS - 1] = A + B;
+}
+
+/* Montgomery c[] = a[] * b[] / R % key. */
+static void montMul(const uint32_t *key,
+ uint32_t *c, const uint32_t *a,
+ const uint32_t *b)
+{
+ int i;
+
+ for (i = 0; i < RSA_NUM_WORDS; ++i)
+ c[i] = 0;
+
+ for (i = 0; i < RSA_NUM_WORDS; ++i)
+ montMulAdd(key, c, a[i], b);
+}
+
+/* Montgomery c[] = a[] * 1 / R % key. */
+static void montMul1(const uint32_t *key,
+ uint32_t *c, const uint32_t *a)
+{
+ int i;
+
+ for (i = 0; i < RSA_NUM_WORDS; ++i)
+ c[i] = 0;
+
+ montMulAdd(key, c, 1, a);
+ for (i = 1; i < RSA_NUM_WORDS; ++i)
+ montMulAdd(key, c, 0, a);
+}
+
+#if LOADERKEYEXP == 3
+/* In-place exponentiation to power 3 % key. */
+static void modpow(const uint32_t *key,
+ const uint32_t *signature, uint32_t *out)
+{
+ static uint32_t aaR[RSA_NUM_WORDS];
+ static uint32_t aaaR[RSA_NUM_WORDS];
+
+ montMul(key, aaR, signature, signature);
+ montMul(key, aaaR, aaR, signature);
+ montMul1(key, out, aaaR);
+}
+#endif
+void LOADERKEY_verify(uint32_t keyid, const uint32_t *signature,
+ const uint32_t *sha256)
+{
+ static uint32_t buf[RSA_NUM_WORDS]
+ __attribute__((section(".guarded_data")));
+ static uint32_t hash[SHA256_DIGEST_WORDS]
+ __attribute__((section(".guarded_data")));
+ uint32_t step, offset;
+ const uint32_t *key;
+ int i;
+
+ if (keyid == LOADERKEY_B[0])
+ key = LOADERKEY_B;
+ else
+ key = LOADERKEY_A;
+
+ modpow(key, signature, buf);
+ VERBOSE("sig %.384h\n", buf);
+
+ /*
+ * XOR in offsets across buf. Mostly to get rid of all those -1 words
+ * in there.
+ */
+ offset = rand() % RSA_NUM_WORDS;
+ step = (RANDOM_STEP % RSA_NUM_WORDS) || 1;
+
+ for (i = 0; i < RSA_NUM_WORDS; ++i) {
+ buf[offset] ^= (0x1000u + offset);
+ offset = (offset + step) % RSA_NUM_WORDS;
+ }
+
+ /*
+ * Xor digest location, so all words becomes 0 only iff equal.
+ *
+ * Also XOR in offset and non-zero const. This to avoid repeat
+ * glitches to zero be able to produce the right result.
+ */
+ offset = rand() % SHA256_DIGEST_WORDS;
+ step = (RANDOM_STEP % SHA256_DIGEST_WORDS) || 1;
+ for (i = 0; i < SHA256_DIGEST_WORDS; ++i) {
+ buf[offset] ^= bswap(sha256[SHA256_DIGEST_WORDS - 1 - offset])
+ ^ (offset + 0x10u);
+ offset = (offset + step) % SHA256_DIGEST_WORDS;
+ }
+
+ VERBOSE("\nsig^ %.384h\n\n", buf);
+
+ /* Hash resulting buffer. */
+ hwSHA256(buf, RSA_NUM_BYTES, hash);
+
+ VERBOSE("hash %.32h\n", hash);
+
+ /*
+ * Write computed hash to unlock register to unlock execution, iff
+ * right. Idea is that this flow cannot be glitched to have correct
+ * values with any probability.
+ */
+ for (i = 0; i < SHA256_DIGEST_WORDS; ++i)
+ GREG32_ADDR(GLOBALSEC, SB_BL_SIG0)[i] = hash[i];
+
+ /*
+ * Make an unlock attempt. Value written is irrelevant, as long as
+ * something is written.
+ */
+ GREG32(GLOBALSEC, SIG_UNLOCK) = 1;
+}
diff --git a/chip/g/loader/verify.h b/chip/g/loader/verify.h
new file mode 100644
index 0000000000..74988dc605
--- /dev/null
+++ b/chip/g/loader/verify.h
@@ -0,0 +1,16 @@
+/* Copyright 2015 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_CHIP_G_LOADER_VERIFY_H
+#define __EC_CHIP_G_LOADER_VERIFY_H
+
+/*
+ * Verify a RSA PKCS1.5 signature against an expected sha256. Unlocks for
+ * execution upon success.
+ */
+void LOADERKEY_verify(uint32_t keyid,
+ const uint32_t *signature, const uint32_t *sha256);
+
+#endif /* __EC_CHIP_G_LOADER_VERIFY_H */