summaryrefslogtreecommitdiff
path: root/chip/g/dcrypto/dcrypto_runtime.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/g/dcrypto/dcrypto_runtime.c')
-rw-r--r--chip/g/dcrypto/dcrypto_runtime.c480
1 files changed, 480 insertions, 0 deletions
diff --git a/chip/g/dcrypto/dcrypto_runtime.c b/chip/g/dcrypto/dcrypto_runtime.c
new file mode 100644
index 0000000000..394293ab83
--- /dev/null
+++ b/chip/g/dcrypto/dcrypto_runtime.c
@@ -0,0 +1,480 @@
+/* Copyright 2016 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 "flash_log.h"
+#include "internal.h"
+#include "registers.h"
+#include "task.h"
+
+#define DMEM_NUM_WORDS 1024
+#define IMEM_NUM_WORDS 1024
+
+static struct mutex dcrypto_mutex;
+static volatile task_id_t my_task_id;
+static uint8_t dcrypto_is_initialized;
+
+static const uint32_t wiped_value = 0xdddddddd;
+
+static void dcrypto_reset_and_wipe(void)
+{
+ int i;
+ volatile uint32_t *ptr;
+
+ /* Reset. */
+ GREG32(CRYPTO, CONTROL) = GC_CRYPTO_CONTROL_RESET_MASK;
+ GREG32(CRYPTO, CONTROL) = 0;
+
+ /* Reset all the status bits. */
+ GREG32(CRYPTO, INT_STATE) = -1;
+
+ /* Wipe state. */
+ GREG32(CRYPTO, WIPE_SECRETS) = 1;
+
+ /* Wipe DMEM. */
+ ptr = GREG32_ADDR(CRYPTO, DMEM_DUMMY);
+ for (i = 0; i < DMEM_NUM_WORDS; ++i)
+ *ptr++ = wiped_value;
+}
+
+static void dcrypto_wipe_imem(void)
+{
+ int i;
+ volatile uint32_t *ptr;
+
+ /* Wipe IMEM. */
+ ptr = GREG32_ADDR(CRYPTO, IMEM_DUMMY);
+ for (i = 0; i < IMEM_NUM_WORDS; ++i)
+ *ptr++ = wiped_value;
+}
+
+void dcrypto_init_and_lock(void)
+{
+ mutex_lock(&dcrypto_mutex);
+ my_task_id = task_get_current();
+
+ if (dcrypto_is_initialized)
+ return;
+
+ /* Enable PMU. */
+ REG_WRITE_MLV(GR_PMU_PERICLKSET0, GC_PMU_PERICLKSET0_DCRYPTO0_CLK_MASK,
+ GC_PMU_PERICLKSET0_DCRYPTO0_CLK_LSB, 1);
+
+ dcrypto_reset_and_wipe();
+ dcrypto_wipe_imem();
+
+ /* Turn off random nops (which are enabled by default). */
+ GWRITE_FIELD(CRYPTO, RAND_STALL_CTL, STALL_EN, 0);
+ /* Configure random nop percentage at 6%. */
+ GWRITE_FIELD(CRYPTO, RAND_STALL_CTL, FREQ, 3);
+ /* Now turn on random nops. */
+ GWRITE_FIELD(CRYPTO, RAND_STALL_CTL, STALL_EN, 1);
+
+ GREG32(CRYPTO, INT_STATE) = -1; /* Reset all the status bits. */
+ GREG32(CRYPTO, INT_ENABLE) = -1; /* Enable all status bits. */
+
+ task_enable_irq(GC_IRQNUM_CRYPTO0_HOST_CMD_DONE_INT);
+
+ dcrypto_is_initialized = 1;
+}
+
+void dcrypto_unlock(void)
+{
+ mutex_unlock(&dcrypto_mutex);
+}
+
+#ifndef DCRYPTO_CALL_TIMEOUT_US
+#define DCRYPTO_CALL_TIMEOUT_US (700 * 1000)
+#endif
+/*
+ * When running on Cr50 this event belongs in the TPM task event space. Make
+ * sure there is no collision with events defined in ./common/tpm_registers.c.
+ */
+#define TASK_EVENT_DCRYPTO_DONE TASK_EVENT_CUSTOM_BIT(0)
+
+uint32_t dcrypto_call(uint32_t adr)
+{
+ uint32_t event;
+ uint32_t state = 0;
+
+ do {
+ /* Reset all the status bits. */
+ GREG32(CRYPTO, INT_STATE) = -1;
+ } while (GREG32(CRYPTO, INT_STATE) & 3);
+
+ GREG32(CRYPTO, HOST_CMD) = 0x08000000 + adr; /* Call imem:adr. */
+
+ event = task_wait_event_mask(TASK_EVENT_DCRYPTO_DONE,
+ DCRYPTO_CALL_TIMEOUT_US);
+ /* TODO(ngm): switch return value to an enum. */
+ switch (event) {
+ case TASK_EVENT_DCRYPTO_DONE:
+ /*
+ * We expect only the CMD_RECV status bit to be set at this
+ * point. CMD_DONE got cleared in the interrupt handler. Any and
+ * all other bits are indicative of error.
+ * Except for MOD_OPERAND_OUT_OF_RANGE, which is noise.
+ */
+ state = GREG32(CRYPTO, INT_STATE);
+ if ((state &
+ ~(GC_CRYPTO_INT_STATE_MOD_OPERAND_OUT_OF_RANGE_MASK |
+ GC_CRYPTO_INT_STATE_HOST_CMD_RECV_MASK)) == 0)
+ return 0;
+ /* fall through */
+ default:
+ dcrypto_reset_and_wipe();
+#ifdef CONFIG_FLASH_LOG
+ /* State value of zero indicates event timeout. */
+ flash_log_add_event(FE_LOG_DCRYPTO_FAILURE,
+ sizeof(state), &state);
+#endif
+ return 1;
+ }
+}
+
+void __keep dcrypto_done_interrupt(void)
+{
+ GREG32(CRYPTO, INT_STATE) = GC_CRYPTO_INT_STATE_HOST_CMD_DONE_MASK;
+ task_set_event(my_task_id, TASK_EVENT_DCRYPTO_DONE, 0);
+}
+DECLARE_IRQ(GC_IRQNUM_CRYPTO0_HOST_CMD_DONE_INT, dcrypto_done_interrupt, 1);
+
+void dcrypto_imem_load(size_t offset, const uint32_t *opcodes,
+ size_t n_opcodes)
+{
+ size_t i;
+ volatile uint32_t *ptr = GREG32_ADDR(CRYPTO, IMEM_DUMMY);
+
+ ptr += offset;
+ /* Check first word and copy all only if different. */
+ if (ptr[0] != opcodes[0]) {
+ for (i = 0; i < n_opcodes; ++i)
+ ptr[i] = opcodes[i];
+ }
+}
+
+uint32_t dcrypto_dmem_load(size_t offset, const void *words, size_t n_words)
+{
+ size_t i;
+ volatile uint32_t *ptr = GREG32_ADDR(CRYPTO, DMEM_DUMMY);
+ const uint32_t *src = (const uint32_t *) words;
+ struct access_helper *word_accessor = (struct access_helper *) src;
+ uint32_t diff = 0;
+
+ ptr += offset * 8; /* Offset is in 256 bit addresses. */
+ for (i = 0; i < n_words; ++i) {
+ /*
+ * The implementation of memcpy makes unaligned writes if src
+ * is unaligned. DMEM on the other hand requires writes to be
+ * aligned, so do a word-by-word copy manually here.
+ */
+ uint32_t v = word_accessor[i].udata;
+
+ diff |= (ptr[i] ^ v);
+ ptr[i] = v;
+ }
+ return diff;
+}
+
+#ifdef CRYPTO_TEST_SETUP
+
+#include "console.h"
+#include "dcrypto.h"
+#include "trng.h"
+#include "shared_mem.h"
+#include "system.h"
+#include "watchdog.h"
+
+/* AUTO-GENERATED. DO NOT MODIFY. */
+/* clang-format off */
+static const uint32_t IMEM_test_hang[] = {
+/* @0x0: function forever[2] { */
+#define CF_forever_adr 0
+/*forever: */
+ 0x10080000, /* b forever */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x2: function func17[2] { */
+#define CF_func17_adr 2
+ 0x08000000, /* call &forever */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x4: function func16[2] { */
+#define CF_func16_adr 4
+ 0x08000002, /* call &func17 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x6: function func15[2] { */
+#define CF_func15_adr 6
+ 0x08000004, /* call &func16 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x8: function func14[2] { */
+#define CF_func14_adr 8
+ 0x08000006, /* call &func15 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0xa: function func13[2] { */
+#define CF_func13_adr 10
+ 0x08000008, /* call &func14 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0xc: function func12[2] { */
+#define CF_func12_adr 12
+ 0x0800000a, /* call &func13 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0xe: function func11[2] { */
+#define CF_func11_adr 14
+ 0x0800000c, /* call &func12 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x10: function func10[2] { */
+#define CF_func10_adr 16
+ 0x0800000e, /* call &func11 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x12: function func9[2] { */
+#define CF_func9_adr 18
+ 0x08000010, /* call &func10 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x14: function func8[2] { */
+#define CF_func8_adr 20
+ 0x08000012, /* call &func9 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x16: function func7[2] { */
+#define CF_func7_adr 22
+ 0x08000014, /* call &func8 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x18: function func6[2] { */
+#define CF_func6_adr 24
+ 0x08000016, /* call &func7 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x1a: function func5[2] { */
+#define CF_func5_adr 26
+ 0x08000018, /* call &func6 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x1c: function func4[2] { */
+#define CF_func4_adr 28
+ 0x0800001a, /* call &func5 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x1e: function func3[2] { */
+#define CF_func3_adr 30
+ 0x0800001c, /* call &func4 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x20: function func2[2] { */
+#define CF_func2_adr 32
+ 0x0800001e, /* call &func3 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x22: function func1[2] { */
+#define CF_func1_adr 34
+ 0x08000020, /* call &func2 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x24: function test[2] { */
+#define CF_test_adr 36
+ 0x08000022, /* call &func1 */
+ 0x0c000000, /* ret */
+/* } */
+/* @0x26: function sigchk[2] { */
+#define CF_sigchk_adr 38
+ 0xf8000004, /* sigini #4 */
+ 0xf9ccc3c2, /* sigchk #13419458 */
+/* } */
+};
+/* clang-format on */
+
+/*
+ * Add console command "dcrypto_test" that runs a couple of engine failure
+ * scenarios and checks for adequate handling thereof:
+ * - error return code
+ * - dmem erasure on error
+ * - dmem preservation on success
+ */
+static int command_dcrypto_test(int argc, char *argv[])
+{
+ volatile uint32_t *ptr = GREG32_ADDR(CRYPTO, DMEM_DUMMY);
+ uint32_t not_wiped = ~wiped_value;
+ int result;
+
+ dcrypto_init_and_lock();
+ dcrypto_imem_load(0, IMEM_test_hang, ARRAY_SIZE(IMEM_test_hang));
+
+ *ptr = not_wiped;
+ result = dcrypto_call(CF_func2_adr); /* max legal stack, into hang */
+ if (result != 1 || *ptr != wiped_value)
+ ccprintf("dcrypto_test: fail1 %d,%08x\n", result, *ptr);
+
+ *ptr = not_wiped;
+ result = dcrypto_call(CF_test_adr); /* stack overflow */
+ if (result != 1 || *ptr != wiped_value)
+ ccprintf("dcrypto_test: fail2 %d,%08x\n", result, *ptr);
+
+ *ptr = not_wiped;
+ result = dcrypto_call(CF_sigchk_adr); /* cfi trap */
+ if (result != 1 || *ptr != wiped_value)
+ ccprintf("dcrypto_test: fail3 %d,%08x\n", result, *ptr);
+
+ *ptr = not_wiped;
+ result = dcrypto_call(CF_test_adr + 1); /* simple ret should succeed */
+ if (result != 0 || *ptr != not_wiped)
+ ccprintf("dcrypto_test: fail4 %d,%08x\n", result, *ptr);
+
+ dcrypto_unlock();
+
+ return EC_SUCCESS;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(dcrypto_test, command_dcrypto_test, "",
+ "dcrypto test");
+
+#define ECDSA_TEST_ITERATIONS 1000
+
+#define ECDSA_TEST_SLEEP_DELAY_IN_US 1000000
+
+static const p256_int r_golden = {
+ .a = { 0xebc04580, 0x996c8634, 0xeaff3cd6, 0x4af33b39, 0xa17da3fb,
+ 0x2c9054f4, 0x3b4dfb95, 0xb3bf339c },
+};
+static const p256_int s_golden = {
+ .a = { 0xac457a6d, 0x8ca854ea, 0xa5877cc1, 0x17bd44f2, 0x77c4c11a,
+ 0xd55d07a0, 0x1efb1274, 0x94afb5c9 },
+};
+
+static int call_on_bigger_stack(uint32_t stack,
+ int (*func)(p256_int *, p256_int *),
+ p256_int *r, p256_int *s)
+{
+ int result = 0;
+
+ /* Move to new stack and call the function */
+ __asm__ volatile("mov r4, sp\n"
+ "mov sp, %[new_stack]\n"
+ "mov r0, %[r]\n"
+ "mov r1, %[s]\n"
+ "blx %[func]\n"
+ "mov sp, r4\n"
+ "mov %[result], r0\n"
+ : [result] "=r"(result) /* output */
+ : [new_stack] "r"(stack), [r] "r"(r), [s] "r"(s),
+ [func] "r"(func) /* input */
+ : "r0", "r1", "r2", "r3", "r4",
+ "lr" /* clobbered registers */
+ );
+
+ return result;
+}
+
+/* Sets up the ecdsa_sign function with proper input conditions to mimic the
+ * ecdsa_verisign execution flow.
+ * in: r - ptr to entropy, s - ptr to message.
+ * out: r,s - generated signature.
+ */
+static int ecdsa_sign_go(p256_int *r, p256_int *s)
+{
+ struct drbg_ctx drbg;
+ p256_int d, tmp;
+ int ret = 0;
+ p256_int message = *s;
+
+ /* drbg init with same entropy */
+ hmac_drbg_init(&drbg, r->a, sizeof(r->a), NULL, 0, NULL, 0);
+
+ /* pick a key */
+ ret = dcrypto_p256_pick(&drbg, &tmp);
+ if (ret) {
+ /* to be consistent with ecdsa_sign error return */
+ ret = 0;
+ goto exit;
+ }
+
+ /* add 1 */
+ p256_add_d(&tmp, 1, &d);
+
+ /* drbg_reseed with entropy and message */
+ hmac_drbg_reseed(&drbg, r->a, sizeof(r->a), s->a, sizeof(s->a), NULL,
+ 0);
+
+ ret = dcrypto_p256_ecdsa_sign(&drbg, &d, &message, r, s);
+
+exit:
+ drbg_exit(&drbg);
+ return ret;
+}
+
+static int command_dcrypto_ecdsa_test(int argc, char *argv[])
+{
+ p256_int entropy, message, r, s;
+ LITE_SHA256_CTX hsh;
+ int result = 0;
+ char *new_stack;
+ const uint32_t new_stack_size = 2 * 1024;
+
+ /* start with some known value for a message */
+ const uint8_t ten = 0x0A;
+
+ for (uint8_t i = 0; i < 8; i++)
+ entropy.a[i] = i;
+
+ DCRYPTO_SHA256_init(&hsh, 0);
+ HASH_update(&hsh, &ten, sizeof(ten));
+ p256_from_bin(HASH_final(&hsh), &message);
+
+ r = entropy;
+ s = message;
+
+ result = shared_mem_acquire(new_stack_size, &new_stack);
+
+ if (result != EC_SUCCESS) {
+ ccprintf("Failed to acquire stack memory: %d\n", result);
+ return result;
+ }
+
+ for (uint32_t i = 0; i < ECDSA_TEST_ITERATIONS; i++) {
+ result = call_on_bigger_stack((uint32_t)new_stack +
+ new_stack_size,
+ ecdsa_sign_go, &r, &s);
+
+ if (!result) {
+ ccprintf("ECDSA TEST fail: %d\n", result);
+ return EC_ERROR_INVAL;
+ }
+
+ watchdog_reload();
+ delay_sleep_by(ECDSA_TEST_SLEEP_DELAY_IN_US);
+ }
+
+ shared_mem_release(new_stack);
+
+ /* compare to the golden r and s values */
+ for (uint8_t i = 0; i < 8; i++) {
+ if (r.a[i] != r_golden.a[i]) {
+ ccprintf("ECDSA TEST r does not match with golden at "
+ "%d: %08x != %08x\n",
+ i, r.a[i], r_golden.a[i]);
+ return EC_ERROR_INVAL;
+ }
+ if (s.a[i] != s_golden.a[i]) {
+ ccprintf("ECDSA TEST s does not match with golden at "
+ "%d: %08x != %08x\n",
+ i, s.a[i], s_golden.a[i]);
+ return EC_ERROR_INVAL;
+ }
+ }
+
+ ccprintf("ECDSA TEST success!!!\n");
+
+ return EC_SUCCESS;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(dcrypto_ecdsa, command_dcrypto_ecdsa_test, "",
+ "dcrypto ecdsa test");
+
+#endif