summaryrefslogtreecommitdiff
path: root/board/cr50/dcrypto/sha_hw.c
diff options
context:
space:
mode:
Diffstat (limited to 'board/cr50/dcrypto/sha_hw.c')
-rw-r--r--board/cr50/dcrypto/sha_hw.c265
1 files changed, 265 insertions, 0 deletions
diff --git a/board/cr50/dcrypto/sha_hw.c b/board/cr50/dcrypto/sha_hw.c
new file mode 100644
index 0000000000..acad1ba29d
--- /dev/null
+++ b/board/cr50/dcrypto/sha_hw.c
@@ -0,0 +1,265 @@
+/* Copyright 2021 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 "dcrypto.h"
+#include "internal.h"
+#include "registers.h"
+
+#ifdef SECTION_IS_RO
+/* RO is single threaded. */
+#define mutex_lock(x)
+#define mutex_unlock(x)
+static inline int dcrypto_grab_sha_hw(void)
+{
+ return 1;
+}
+static inline void dcrypto_release_sha_hw(void)
+{
+}
+#else
+#include "task.h"
+static struct mutex hw_busy_mutex;
+
+static bool hw_busy;
+
+int dcrypto_grab_sha_hw(void)
+{
+ int rv = 0;
+
+ mutex_lock(&hw_busy_mutex);
+ if (!hw_busy) {
+ rv = 1;
+ hw_busy = true;
+ }
+ mutex_unlock(&hw_busy_mutex);
+
+ return rv;
+}
+
+void dcrypto_release_sha_hw(void)
+{
+ mutex_lock(&hw_busy_mutex);
+ hw_busy = false;
+ mutex_unlock(&hw_busy_mutex);
+}
+
+#endif /* ! SECTION_IS_RO */
+
+static void dcrypto_sha_wait(enum sha_mode mode, uint32_t *digest)
+{
+ int i;
+ const int digest_len = (mode == SHA1_MODE) ? SHA1_DIGEST_SIZE :
+ SHA256_DIGEST_SIZE;
+
+ /* Stop LIVESTREAM mode. */
+ GREG32(KEYMGR, SHA_TRIG) = GC_KEYMGR_SHA_TRIG_TRIG_STOP_MASK;
+
+ /* Wait for SHA DONE interrupt. */
+ while (!GREG32(KEYMGR, SHA_ITOP))
+ ;
+
+ /* Read out final digest. */
+ for (i = 0; i < digest_len / 4; ++i)
+ *digest++ = GR_KEYMGR_SHA_HASH(i);
+ dcrypto_release_sha_hw();
+}
+
+static void dcrypto_sha_update(union hash_ctx *unused, const void *data,
+ size_t n)
+{
+ const uint8_t *bp = (const uint8_t *)data;
+ const uint32_t *wp;
+
+ /* Feed unaligned start bytes. */
+ while (n != 0 && ((uint32_t)bp & 3)) {
+ GREG8(KEYMGR, SHA_INPUT_FIFO) = *bp++;
+ n -= 1;
+ }
+
+ /* Feed groups of aligned words. */
+ wp = (uint32_t *)bp;
+ while (n >= 8 * 4) {
+ 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 -= 8 * 4;
+ }
+ /* Feed individual aligned words. */
+ while (n >= 4) {
+ GREG32(KEYMGR, SHA_INPUT_FIFO) = *wp++;
+ n -= 4;
+ }
+
+ /* Feed remaining bytes. */
+ bp = (uint8_t *)wp;
+ while (n != 0) {
+ GREG8(KEYMGR, SHA_INPUT_FIFO) = *bp++;
+ n -= 1;
+ }
+}
+
+static void dcrypto_sha_init(enum sha_mode mode)
+{
+ int val;
+
+ /* Stop LIVESTREAM mode, in case final() was not called. */
+ GREG32(KEYMGR, SHA_TRIG) = GC_KEYMGR_SHA_TRIG_TRIG_STOP_MASK;
+ /* Clear interrupt status. */
+ GREG32(KEYMGR, SHA_ITOP) = 0;
+
+ /* Enable streaming mode. */
+ val = GC_KEYMGR_SHA_CFG_EN_LIVESTREAM_MASK;
+ /* Enable SHA DONE interrupt. */
+ val |= GC_KEYMGR_SHA_CFG_EN_INT_EN_DONE_MASK;
+ /* Select SHA mode. */
+ if (mode == SHA1_MODE)
+ val |= GC_KEYMGR_SHA_CFG_EN_SHA1_MASK;
+ GREG32(KEYMGR, SHA_CFG_EN) = val;
+
+ /* Turn off random nops (which are enabled by default). */
+ GWRITE_FIELD(KEYMGR, SHA_RAND_STALL_CTL, STALL_EN, 0);
+ /* Configure random nop percentage at 12%. */
+ GWRITE_FIELD(KEYMGR, SHA_RAND_STALL_CTL, FREQ, 2);
+ /* Now turn on random nops. */
+ GWRITE_FIELD(KEYMGR, SHA_RAND_STALL_CTL, STALL_EN, 1);
+
+ /* Start SHA engine. */
+ GREG32(KEYMGR, SHA_TRIG) = GC_KEYMGR_SHA_TRIG_TRIG_GO_MASK;
+}
+
+static const struct sha1_digest *dcrypto_sha1_final(union hash_ctx *ctx)
+{
+ dcrypto_sha_wait(SHA1_MODE, ctx->sha1.digest.b32);
+ return &ctx->sha1.digest;
+}
+
+static const struct sha256_digest *dcrypto_sha256_final(union hash_ctx *ctx)
+{
+ dcrypto_sha_wait(SHA256_MODE, ctx->sha256.digest.b32);
+ return &ctx->sha256.digest;
+}
+
+static const union sha_digests *dcrypto_sha256_final_as_hash(
+ union hash_ctx *const ctx) __alias(dcrypto_sha256_final);
+static const union sha_digests *dcrypto_sha1_final_as_hash(
+ union hash_ctx *const ctx) __alias(dcrypto_sha1_final);
+
+static void dcrypto_sha_hash(enum sha_mode mode, const uint8_t *data, size_t n,
+ uint32_t *digest)
+{
+ dcrypto_sha_init(mode);
+ dcrypto_sha_update(NULL, data, n);
+ dcrypto_sha_wait(mode, digest);
+}
+
+/* Requires dcrypto_grab_sha_hw() to be called first. */
+static void dcrypto_sha1_init(union hash_ctx *ctx)
+{
+ static const struct hash_vtable hw_sha1_vtab = {
+ dcrypto_sha1_init, dcrypto_sha_update,
+ dcrypto_sha1_final_as_hash, HMAC_sw_final,
+ SHA1_DIGEST_SIZE, SHA1_BLOCK_SIZE,
+ sizeof(struct sha1_ctx)
+ };
+
+ ctx->f = &hw_sha1_vtab;
+ dcrypto_sha_init(SHA1_MODE);
+}
+
+static void dcrypto_sha256_init(union hash_ctx *ctx)
+{
+ /* Hardware SHA implementation. */
+ static const struct hash_vtable HW_SHA256_VTAB = {
+ dcrypto_sha256_init, dcrypto_sha_update,
+ dcrypto_sha256_final_as_hash, HMAC_sw_final,
+ SHA256_DIGEST_SIZE, SHA256_BLOCK_SIZE,
+ sizeof(struct sha256_ctx)
+ };
+
+ ctx->f = &HW_SHA256_VTAB;
+ dcrypto_sha_init(SHA256_MODE);
+}
+
+/**
+ * Select and initialize either the software or hardware
+ * implementation. If "multi-threaded" behaviour is required, then
+ * callers must specifically use software version SHA1_sw_init(). This
+ * is because SHA1 state internal to the hardware cannot be extracted, so
+ * it is not possible to suspend and resume a hardware based SHA operation.
+ *
+ * Hardware implementation is selected based on availability. Hardware is
+ * considered to be in use between init() and finished() calls. If hardware
+ * is not available, fall back to software implementation.
+ */
+void SHA1_hw_init(struct sha1_ctx *ctx)
+{
+ if (dcrypto_grab_sha_hw())
+ dcrypto_sha1_init((union hash_ctx *)ctx);
+ else
+ SHA1_sw_init(ctx);
+}
+
+/**
+ * Select and initialize either the software or hardware
+ * implementation. If "multi-threaded" behaviour is required, then
+ * callers must specifically use software version SHA256_sw_init(). This
+ * is because SHA256 state internal to the hardware cannot be extracted, so
+ * it is not possible to suspend and resume a hardware based SHA operation.
+ *
+ * Hardware implementation is selected based on availability. Hardware is
+ * considered to be in use between init() and finished() calls. If hardware
+ * is not available, fall back to software implementation.
+ */
+void SHA256_hw_init(struct sha256_ctx *ctx)
+{
+ if (dcrypto_grab_sha_hw())
+ dcrypto_sha256_init((union hash_ctx *)ctx);
+ else
+ SHA256_sw_init(ctx);
+}
+
+const struct sha1_digest *SHA1_hw_hash(const void *data, size_t n,
+ struct sha1_digest *digest)
+{
+ if (dcrypto_grab_sha_hw())
+ /* dcrypto_sha_wait() will release the hw. */
+ dcrypto_sha_hash(SHA1_MODE, data, n, digest->b32);
+ else
+ SHA1_sw_hash(data, n, digest);
+ return digest;
+}
+
+const struct sha256_digest *SHA256_hw_hash(const void *data, size_t n,
+ struct sha256_digest *digest)
+{
+ if (dcrypto_grab_sha_hw())
+ /* dcrypto_sha_wait() will release the hw. */
+ dcrypto_sha_hash(SHA256_MODE, data, n, digest->b32);
+ else
+ SHA256_sw_hash(data, n, digest);
+ return digest;
+}
+
+/* For compatibility with chip/g code. */
+const uint8_t *DCRYPTO_SHA1_hash(const void *data, size_t n, uint8_t *digest)
+ __alias(SHA1_hw_hash);
+
+/* TODO(b/195092622): initialize HW HMAC instead. */
+void HMAC_SHA256_hw_init(struct hmac_sha256_ctx *ctx, const void *key,
+ size_t len)
+{
+ SHA256_hw_init(&ctx->hash);
+ HMAC_sw_init((union hmac_ctx *)ctx, key, len);
+}
+
+const struct sha256_digest *
+HMAC_SHA256_hw_final(struct hmac_sha256_ctx *ctx)
+{
+ return HMAC_SHA256_final(ctx);
+}