[sw,crypto] Copy silicon_creator OTBN driver.

This will serve as a starting point for a crypto library driver outside
of silicon_creator.

Signed-off-by: Jade Philipoom <jadep@google.com>
diff --git a/sw/device/lib/crypto/drivers/otbn.c b/sw/device/lib/crypto/drivers/otbn.c
new file mode 100644
index 0000000..c2aae71
--- /dev/null
+++ b/sw/device/lib/crypto/drivers/otbn.c
@@ -0,0 +1,136 @@
+// 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/otbn.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "sw/device/lib/base/bitfield.h"
+#include "sw/device/silicon_creator/lib/base/abs_mmio.h"
+#include "sw/device/silicon_creator/lib/error.h"
+
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+#include "otbn_regs.h"  // Generated.
+
+#define ASSERT_ERR_BIT_MATCH(enum_val, autogen_val) \
+  static_assert(enum_val == 1 << (autogen_val),     \
+                "OTBN register bit doesn't match autogen value.");
+
+ASSERT_ERR_BIT_MATCH(kOtbnErrBitsBadDataAddr, OTBN_ERR_BITS_BAD_DATA_ADDR_BIT);
+ASSERT_ERR_BIT_MATCH(kOtbnErrBitsBadInsnAddr, OTBN_ERR_BITS_BAD_INSN_ADDR_BIT);
+ASSERT_ERR_BIT_MATCH(kOtbnErrBitsCallStack, OTBN_ERR_BITS_CALL_STACK_BIT);
+ASSERT_ERR_BIT_MATCH(kOtbnErrBitsIllegalInsn, OTBN_ERR_BITS_ILLEGAL_INSN_BIT);
+ASSERT_ERR_BIT_MATCH(kOtbnErrBitsLoop, OTBN_ERR_BITS_LOOP_BIT);
+ASSERT_ERR_BIT_MATCH(kOtbnErrBitsImemIntgViolation,
+                     OTBN_ERR_BITS_IMEM_INTG_VIOLATION_BIT);
+ASSERT_ERR_BIT_MATCH(kOtbnErrBitsDmemIntgViolation,
+                     OTBN_ERR_BITS_DMEM_INTG_VIOLATION_BIT);
+ASSERT_ERR_BIT_MATCH(kOtbnErrBitsRegIntgViolation,
+                     OTBN_ERR_BITS_REG_INTG_VIOLATION_BIT);
+ASSERT_ERR_BIT_MATCH(kOtbnErrBitsBusIntgViolation,
+                     OTBN_ERR_BITS_BUS_INTG_VIOLATION_BIT);
+ASSERT_ERR_BIT_MATCH(kOtbnErrBitsIllegalBusAccess,
+                     OTBN_ERR_BITS_ILLEGAL_BUS_ACCESS_BIT);
+ASSERT_ERR_BIT_MATCH(kOtbnErrBitsLifecycleEscalation,
+                     OTBN_ERR_BITS_LIFECYCLE_ESCALATION_BIT);
+ASSERT_ERR_BIT_MATCH(kOtbnErrBitsFatalSoftware,
+                     OTBN_ERR_BITS_FATAL_SOFTWARE_BIT);
+
+const size_t kOtbnDMemSizeBytes = OTBN_DMEM_SIZE_BYTES;
+const size_t kOtbnIMemSizeBytes = OTBN_IMEM_SIZE_BYTES;
+
+enum { kBase = TOP_EARLGREY_OTBN_BASE_ADDR };
+
+/**
+ * Ensures that `offset_bytes` and `len` are valid for a given `mem_size`.
+ */
+static otbn_error_t check_offset_len(uint32_t offset_bytes, size_t num_words,
+                                     size_t mem_size) {
+  if (offset_bytes + num_words * sizeof(uint32_t) <
+          num_words * sizeof(uint32_t) ||
+      offset_bytes + num_words * sizeof(uint32_t) > mem_size) {
+    return kOtbnErrorBadOffsetLen;
+  }
+  return kOtbnErrorOk;
+}
+
+void otbn_execute(void) {
+  abs_mmio_write32(kBase + OTBN_CMD_REG_OFFSET, kOtbnCmdExecute);
+}
+
+bool otbn_is_busy() {
+  uint32_t status = abs_mmio_read32(kBase + OTBN_STATUS_REG_OFFSET);
+  return status != kOtbnStatusIdle && status != kOtbnStatusLocked;
+}
+
+void otbn_get_err_bits(otbn_err_bits_t *err_bits) {
+  *err_bits = abs_mmio_read32(kBase + OTBN_ERR_BITS_REG_OFFSET);
+}
+
+otbn_error_t otbn_imem_write(uint32_t offset_bytes, const uint32_t *src,
+                             size_t num_words) {
+  OTBN_RETURN_IF_ERROR(
+      check_offset_len(offset_bytes, num_words, kOtbnIMemSizeBytes));
+
+  for (size_t i = 0; i < num_words; ++i) {
+    abs_mmio_write32(
+        kBase + OTBN_IMEM_REG_OFFSET + offset_bytes + i * sizeof(uint32_t),
+        src[i]);
+  }
+
+  return kOtbnErrorOk;
+}
+
+otbn_error_t otbn_dmem_write(uint32_t offset_bytes, const uint32_t *src,
+                             size_t num_words) {
+  OTBN_RETURN_IF_ERROR(
+      check_offset_len(offset_bytes, num_words, kOtbnDMemSizeBytes));
+
+  for (size_t i = 0; i < num_words; ++i) {
+    abs_mmio_write32(
+        kBase + OTBN_DMEM_REG_OFFSET + offset_bytes + i * sizeof(uint32_t),
+        src[i]);
+  }
+
+  return kOtbnErrorOk;
+}
+
+otbn_error_t otbn_dmem_read(uint32_t offset_bytes, uint32_t *dest,
+                            size_t num_words) {
+  OTBN_RETURN_IF_ERROR(
+      check_offset_len(offset_bytes, num_words, kOtbnDMemSizeBytes));
+
+  for (size_t i = 0; i < num_words; ++i) {
+    dest[i] = abs_mmio_read32(kBase + OTBN_DMEM_REG_OFFSET + offset_bytes +
+                              i * sizeof(uint32_t));
+  }
+
+  return kOtbnErrorOk;
+}
+
+void otbn_zero_dmem(void) {
+  for (size_t i = 0; i < kOtbnDMemSizeBytes; i += sizeof(uint32_t)) {
+    abs_mmio_write32(kBase + OTBN_DMEM_REG_OFFSET + i, 0u);
+  }
+}
+
+otbn_error_t otbn_set_ctrl_software_errs_fatal(bool enable) {
+  // Only one bit in the CTRL register so no need to read current value.
+  uint32_t new_ctrl;
+
+  if (enable) {
+    new_ctrl = 1;
+  } else {
+    new_ctrl = 0;
+  }
+
+  abs_mmio_write32(kBase + OTBN_CTRL_REG_OFFSET, new_ctrl);
+  if (abs_mmio_read32(kBase + OTBN_CTRL_REG_OFFSET) != new_ctrl) {
+    return kOtbnErrorUnavailable;
+  }
+
+  return kOtbnErrorOk;
+}
diff --git a/sw/device/lib/crypto/drivers/otbn.h b/sw/device/lib/crypto/drivers/otbn.h
new file mode 100644
index 0000000..46b7d53
--- /dev/null
+++ b/sw/device/lib/crypto/drivers/otbn.h
@@ -0,0 +1,193 @@
+// 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_OTBN_H_
+#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_OTBN_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The size of OTBN's data memory in bytes.
+ */
+extern const size_t kOtbnDMemSizeBytes;
+
+/**
+ * The size of OTBN's instruction memory in bytes.
+ */
+extern const size_t kOtbnIMemSizeBytes;
+
+/**
+ * OTBN commands
+ */
+typedef enum dif_otbn_cmd {
+  kOtbnCmdExecute = 0xd8,
+  kOtbnCmdSecWipeDmem = 0xc3,
+  kOtbnCmdSecWipeImem = 0x1e,
+} otbn_cmd_t;
+
+/**
+ * OTBN status
+ */
+typedef enum otbn_status {
+  kOtbnStatusIdle = 0x00,
+  kOtbnStatusBusyExecute = 0x01,
+  kOtbnStatusBusySecWipeDmem = 0x02,
+  kOtbnStatusBusySecWipeImem = 0x03,
+  kOtbnStatusLocked = 0xFF,
+} otbn_status_t;
+
+/**
+ * Error codes for the OTBN driver.
+ */
+typedef enum otbn_error_t {
+  /** No errors. */
+  kOtbnErrorOk = 0,
+  /** Invalid argument provided to OTBN interface function. */
+  kOtbnErrorInvalidArgument = 1,
+  /** Invalid offset provided. */
+  kOtbnErrorBadOffsetLen = 2,
+  /** OTBN internal error; use otbn_get_err_bits for specific error codes. */
+  kOtbnErrorExecutionFailed = 3,
+  /** Attempt to interact with OTBN while it was unavailable. */
+  kOtbnErrorUnavailable = 4,
+} otbn_error_t;
+
+/**
+ * Evaluate an expression and return if the result is an error.
+ *
+ * @param expr_ An expression which results in an otbn_error_t.
+ */
+#define OTBN_RETURN_IF_ERROR(expr_)     \
+  do {                                  \
+    otbn_error_t local_error_ = expr_;  \
+    if (local_error_ != kOtbnErrorOk) { \
+      return local_error_;              \
+    }                                   \
+  } while (0)
+
+/**
+ * Start the execution of the application loaded into OTBN.
+ */
+void otbn_execute(void);
+
+/**
+ * Is OTBN busy executing an application?
+ *
+ * @return OTBN is busy
+ */
+bool otbn_is_busy(void);
+
+/**
+ * OTBN Internal Errors
+ *
+ * OTBN uses a bitfield to indicate which errors have been seen. Multiple errors
+ * can be seen at the same time. This enum gives the individual bits that may be
+ * set for different errors.
+ */
+typedef enum otbn_err_bits {
+  kOtbnErrBitsNoError = 0,
+  /** A BAD_DATA_ADDR error was observed. */
+  kOtbnErrBitsBadDataAddr = (1 << 0),
+  /** A BAD_INSN_ADDR error was observed. */
+  kOtbnErrBitsBadInsnAddr = (1 << 1),
+  /** A CALL_STACK error was observed. */
+  kOtbnErrBitsCallStack = (1 << 2),
+  /** An ILLEGAL_INSN error was observed. */
+  kOtbnErrBitsIllegalInsn = (1 << 3),
+  /** A LOOP error was observed. */
+  kOtbnErrBitsLoop = (1 << 4),
+  /** A IMEM_INTG_VIOLATION error was observed. */
+  kOtbnErrBitsImemIntgViolation = (1 << 16),
+  /** A DMEM_INTG_VIOLATION error was observed. */
+  kOtbnErrBitsDmemIntgViolation = (1 << 17),
+  /** A REG_INTG_VIOLATION error was observed. */
+  kOtbnErrBitsRegIntgViolation = (1 << 18),
+  /** A BUS_INTG_VIOLATION error was observed. */
+  kOtbnErrBitsBusIntgViolation = (1 << 19),
+  /** A BAD_INTERNAL_STATE error was observed. */
+  kDifOtbnErrBitsBadInternalState = (1 << 20),
+  /** An ILLEGAL_BUS_ACCESS error was observed. */
+  kOtbnErrBitsIllegalBusAccess = (1 << 21),
+  /** A LIFECYCLE_ESCALATION error was observed. */
+  kOtbnErrBitsLifecycleEscalation = (1 << 22),
+  /** A FATAL_SOFTWARE error was observed. */
+  kOtbnErrBitsFatalSoftware = (1 << 23),
+} otbn_err_bits_t;
+
+/**
+ * Get the error bits set by the device if the operation failed.
+ *
+ * @param[out] err_bits The error bits returned by the hardware.
+ */
+void otbn_get_err_bits(otbn_err_bits_t *err_bits);
+
+/**
+ * Write an OTBN application into its instruction memory (IMEM)
+ *
+ * Only 32b-aligned 32b word accesses are allowed.
+ *
+ * @param offset_bytes the byte offset in IMEM the first word is written to
+ * @param src the main memory location to start reading from.
+ * @param len number of words to copy.
+ * @return `kOtbnErrorBadOffset` if `offset_bytes` isn't word aligned,
+ * `kOtbnErrorBadOffsetLen` if `len` is invalid , `kOtbnErrorOk` otherwise.
+ */
+otbn_error_t otbn_imem_write(uint32_t offset_bytes, const uint32_t *src,
+                             size_t len);
+
+/**
+ * Write to OTBN's data memory (DMEM)
+ *
+ * Only 32b-aligned 32b word accesses are allowed.
+ *
+ * @param offset_bytes the byte offset in DMEM the first word is written to
+ * @param src the main memory location to start reading from.
+ * @param len number of words to copy.
+ * @return `kOtbnErrorBadOffset` if `offset_bytes` isn't word aligned,
+ * `kOtbnErrorBadOffsetLen` if `len` is invalid , `kOtbnErrorOk` otherwise.
+ */
+otbn_error_t otbn_dmem_write(uint32_t offset_bytes, const uint32_t *src,
+                             size_t len);
+
+/**
+ * Read from OTBN's data memory (DMEM)
+ *
+ * Only 32b-aligned 32b word accesses are allowed.
+ *
+ * @param offset_bytes the byte offset in DMEM the first word is read from
+ * @param[out] dest the main memory location to copy the data to (preallocated)
+ * @param len number of words to copy.
+ * @return `kOtbnErrorBadOffset` if `offset_bytes` isn't word aligned,
+ * `kOtbnErrorBadOffsetLen` if `len` is invalid , `kOtbnErrorOk` otherwise.
+ */
+otbn_error_t otbn_dmem_read(uint32_t offset_bytes, uint32_t *dest, size_t len);
+
+/**
+ * Zero out the contents of OTBN's data memory (DMEM).
+ */
+void otbn_zero_dmem(void);
+
+/**
+ * Sets the software errors are fatal bit in the control register.
+ *
+ * When set any software error becomes a fatal error. The bit can only be
+ * changed when the OTBN status is IDLE.
+ *
+ * @param enable Enable or disable whether software errors are fatal.
+ * @return `kOtbnErrorUnavailable` if the requested change cannot be made or
+ * `kOtbnErrorOk` otherwise.
+ */
+otbn_error_t otbn_set_ctrl_software_errs_fatal(bool enable);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_OTBN_H_