[sw] Copy HMAC driver from silicon_creator.
Some things (like the MMIO interface) will need to change, but the
silicon_creator driver is a reasonable starting point for a runtime
driver.
Signed-off-by: Jade Philipoom <jadep@google.com>
diff --git a/sw/device/lib/crypto/drivers/hmac.c b/sw/device/lib/crypto/drivers/hmac.c
new file mode 100644
index 0000000..5e484dc
--- /dev/null
+++ b/sw/device/lib/crypto/drivers/hmac.c
@@ -0,0 +1,91 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/silicon_creator/lib/drivers/hmac.h"
+
+#include "sw/device/lib/base/bitfield.h"
+#include "sw/device/lib/base/macros.h"
+#include "sw/device/lib/base/memory.h"
+#include "sw/device/silicon_creator/lib/base/abs_mmio.h"
+#include "sw/device/silicon_creator/lib/error.h"
+
+#include "hmac_regs.h" // Generated.
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+
+void hmac_sha256_init(void) {
+ // Clear the config, stopping the SHA engine.
+ abs_mmio_write32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_CFG_REG_OFFSET, 0u);
+
+ // Disable and clear interrupts. INTR_STATE register is rw1c.
+ abs_mmio_write32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_INTR_ENABLE_REG_OFFSET,
+ 0u);
+ abs_mmio_write32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_INTR_STATE_REG_OFFSET,
+ UINT32_MAX);
+
+ uint32_t reg = 0;
+ reg = bitfield_bit32_write(reg, HMAC_CFG_DIGEST_SWAP_BIT, false);
+ // Enable endian swap since our inputs are little-endian.
+ reg = bitfield_bit32_write(reg, HMAC_CFG_ENDIAN_SWAP_BIT, true);
+ reg = bitfield_bit32_write(reg, HMAC_CFG_SHA_EN_BIT, true);
+ reg = bitfield_bit32_write(reg, HMAC_CFG_HMAC_EN_BIT, false);
+ abs_mmio_write32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_CFG_REG_OFFSET, reg);
+
+ reg = 0;
+ reg = bitfield_bit32_write(reg, HMAC_CMD_HASH_START_BIT, true);
+ abs_mmio_write32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_CMD_REG_OFFSET, reg);
+}
+
+rom_error_t hmac_sha256_update(const void *data, size_t len) {
+ if (data == NULL) {
+ return kErrorHmacInvalidArgument;
+ }
+ const uint8_t *data_sent = (const uint8_t *)data;
+
+ // Individual byte writes are needed if the buffer isn't word aligned.
+ for (; len != 0 && (uintptr_t)data_sent & 3; --len) {
+ abs_mmio_write8(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_MSG_FIFO_REG_OFFSET,
+ *data_sent++);
+ }
+
+ for (; len >= sizeof(uint32_t); len -= sizeof(uint32_t)) {
+ // FIXME: read_32 does not work for unittests.
+ uint32_t data_aligned = *(const uint32_t *)data_sent;
+ abs_mmio_write32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_MSG_FIFO_REG_OFFSET,
+ data_aligned);
+ data_sent += sizeof(uint32_t);
+ }
+
+ // Handle non-32bit aligned bytes at the end of the buffer.
+ for (; len != 0; --len) {
+ abs_mmio_write8(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_MSG_FIFO_REG_OFFSET,
+ *data_sent++);
+ }
+ return kErrorOk;
+}
+
+rom_error_t hmac_sha256_final(hmac_digest_t *digest) {
+ if (digest == NULL) {
+ return kErrorHmacInvalidArgument;
+ }
+
+ uint32_t reg = 0;
+ reg = bitfield_bit32_write(reg, HMAC_CMD_HASH_PROCESS_BIT, true);
+ abs_mmio_write32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_CMD_REG_OFFSET, reg);
+
+ do {
+ reg = abs_mmio_read32(TOP_EARLGREY_HMAC_BASE_ADDR +
+ HMAC_INTR_STATE_REG_OFFSET);
+ } while (!bitfield_bit32_read(reg, HMAC_INTR_STATE_HMAC_DONE_BIT));
+ abs_mmio_write32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_INTR_STATE_REG_OFFSET,
+ reg);
+
+ // Read the digest in reverse to preserve the numerical value.
+ // The least significant word is at HMAC_DIGEST_7_REG_OFFSET.
+ for (size_t i = 0; i < ARRAYSIZE(digest->digest); ++i) {
+ digest->digest[i] =
+ abs_mmio_read32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_DIGEST_7_REG_OFFSET -
+ (i * sizeof(uint32_t)));
+ }
+ return kErrorOk;
+}
diff --git a/sw/device/lib/crypto/drivers/hmac.h b/sw/device/lib/crypto/drivers/hmac.h
new file mode 100644
index 0000000..34fa97c
--- /dev/null
+++ b/sw/device/lib/crypto/drivers/hmac.h
@@ -0,0 +1,61 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+#ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_HMAC_H_
+#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_HMAC_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "sw/device/silicon_creator/lib/error.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define HMAC_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+
+/**
+ * A typed representation of the HMAC digest.
+ */
+typedef struct hmac_digest {
+ uint32_t digest[8];
+} hmac_digest_t;
+
+/**
+ * Initializes the HMAC in SHA256 mode.
+ *
+ * This function resets the HMAC module to clear the digest register.
+ * It then configures the HMAC block in SHA256 mode with little endian
+ * data input and digest output.
+ */
+void hmac_sha256_init(void);
+
+/**
+ * Sends `len` bytes from `data` to the SHA2-256 function.
+ *
+ * This function does not check for the size of the available HMAC
+ * FIFO. Since the this function is meant to run in blocking mode,
+ * polling for FIFO status is equivalent to stalling on FIFO write.
+ *
+ * @param data Buffer to copy data from.
+ * @param len size of the `data` buffer.
+ * @return The result of the operation.
+ */
+HMAC_WARN_UNUSED_RESULT
+rom_error_t hmac_sha256_update(const void *data, size_t len);
+
+/**
+ * Finalizes SHA256 operation and writes `digest` buffer.
+ *
+ * @param[out] digest Buffer to copy digest to.
+ * @return The result of the operation.
+ */
+HMAC_WARN_UNUSED_RESULT
+rom_error_t hmac_sha256_final(hmac_digest_t *digest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_HMAC_H_