| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| #include "sw/device/lib/dif/dif_otbn.h" |
| |
| #include <assert.h> |
| |
| #include "sw/device/lib/base/bitfield.h" |
| #include "sw/device/lib/base/mmio.h" |
| #include "sw/device/lib/dif/dif_base.h" |
| |
| #include "otbn_regs.h" // Generated. |
| |
| static_assert(kDifOtbnErrBitsBadDataAddr == |
| (1 << OTBN_ERR_BITS_BAD_DATA_ADDR_BIT), |
| "Layout of error bits changed."); |
| static_assert(kDifOtbnErrBitsBadInsnAddr == |
| (1 << OTBN_ERR_BITS_BAD_INSN_ADDR_BIT), |
| "Layout of error bits changed."); |
| static_assert(kDifOtbnErrBitsCallStack == (1 << OTBN_ERR_BITS_CALL_STACK_BIT), |
| "Layout of error bits changed."); |
| static_assert(kDifOtbnErrBitsIllegalInsn == |
| (1 << OTBN_ERR_BITS_ILLEGAL_INSN_BIT), |
| "Layout of error bits changed."); |
| static_assert(kDifOtbnErrBitsLoop == (1 << OTBN_ERR_BITS_LOOP_BIT), |
| "Layout of error bits changed."); |
| static_assert(kDifOtbnErrBitsImemIntgViolation == |
| (1 << OTBN_ERR_BITS_IMEM_INTG_VIOLATION_BIT), |
| "Layout of error bits changed."); |
| static_assert(kDifOtbnErrBitsDmemIntgViolation == |
| (1 << OTBN_ERR_BITS_DMEM_INTG_VIOLATION_BIT), |
| "Layout of error bits changed."); |
| static_assert(kDifOtbnErrBitsRegIntgViolation == |
| (1 << OTBN_ERR_BITS_REG_INTG_VIOLATION_BIT), |
| "Layout of error bits changed."); |
| static_assert(kDifOtbnErrBitsBusIntgViolation == |
| (1 << OTBN_ERR_BITS_BUS_INTG_VIOLATION_BIT), |
| "Layout of error bits changed."); |
| static_assert(kDifOtbnErrBitsIllegalBusAccess == |
| (1 << OTBN_ERR_BITS_ILLEGAL_BUS_ACCESS_BIT), |
| "Layout of error bits changed."); |
| static_assert(kDifOtbnErrBitsLifecycleEscalation == |
| (1 << OTBN_ERR_BITS_LIFECYCLE_ESCALATION_BIT), |
| "Layout of error bits changed."); |
| static_assert(kDifOtbnErrBitsFatalSoftware == |
| (1 << OTBN_ERR_BITS_FATAL_SOFTWARE_BIT), |
| "Layout of error bits changed."); |
| |
| /** |
| * Data width of big number subset, in bytes. |
| */ |
| const int kDifOtbnWlenBytes = 256 / 8; |
| |
| /** |
| * Ensures that `offset` and `size` are valid for a given `mem_size`. |
| * |
| * Valid are 32b word accesses to 32b-aligned memory locations within |
| * `mem_size`. |
| */ |
| static bool check_offset_len(uint32_t offset_bytes, size_t len_bytes, |
| size_t mem_size) { |
| // The overflow check below assumes/requires two unsigned inputs. |
| return (len_bytes % sizeof(uint32_t) == 0 && |
| offset_bytes % sizeof(uint32_t) == 0 && |
| offset_bytes + len_bytes >= len_bytes && |
| offset_bytes + len_bytes <= mem_size); |
| } |
| |
| dif_result_t dif_otbn_reset(const dif_otbn_t *otbn) { |
| if (otbn == NULL) { |
| return kDifBadArg; |
| } |
| |
| mmio_region_write32(otbn->base_addr, OTBN_INTR_ENABLE_REG_OFFSET, 0); |
| |
| // Clear all pending interrupts. |
| mmio_region_write32(otbn->base_addr, OTBN_INTR_STATE_REG_OFFSET, 0xFFFFFFFF); |
| |
| return kDifOk; |
| } |
| |
| dif_result_t dif_otbn_write_cmd(const dif_otbn_t *otbn, dif_otbn_cmd_t cmd) { |
| if (otbn == NULL) { |
| return kDifBadArg; |
| } |
| |
| mmio_region_write32(otbn->base_addr, OTBN_CMD_REG_OFFSET, cmd); |
| |
| return kDifOk; |
| } |
| |
| dif_result_t dif_otbn_get_status(const dif_otbn_t *otbn, |
| dif_otbn_status_t *status) { |
| if (otbn == NULL || status == NULL) { |
| return kDifBadArg; |
| } |
| |
| *status = mmio_region_read32(otbn->base_addr, OTBN_STATUS_REG_OFFSET); |
| |
| return kDifOk; |
| } |
| |
| dif_result_t dif_otbn_get_err_bits(const dif_otbn_t *otbn, |
| dif_otbn_err_bits_t *err_bits) { |
| if (otbn == NULL || err_bits == NULL) { |
| return kDifBadArg; |
| } |
| |
| uint32_t err_bits_raw = |
| mmio_region_read32(otbn->base_addr, OTBN_ERR_BITS_REG_OFFSET); |
| |
| *err_bits = err_bits_raw; |
| return kDifOk; |
| } |
| |
| dif_result_t dif_otbn_get_insn_cnt(const dif_otbn_t *otbn, uint32_t *insn_cnt) { |
| if (otbn == NULL || insn_cnt == NULL) { |
| return kDifBadArg; |
| } |
| |
| *insn_cnt = mmio_region_read32(otbn->base_addr, OTBN_INSN_CNT_REG_OFFSET); |
| return kDifOk; |
| } |
| |
| dif_result_t dif_otbn_imem_write(const dif_otbn_t *otbn, uint32_t offset_bytes, |
| const void *src, size_t len_bytes) { |
| if (otbn == NULL || src == NULL || |
| !check_offset_len(offset_bytes, len_bytes, OTBN_IMEM_SIZE_BYTES)) { |
| return kDifBadArg; |
| } |
| |
| mmio_region_memcpy_to_mmio32( |
| otbn->base_addr, OTBN_IMEM_REG_OFFSET + offset_bytes, src, len_bytes); |
| |
| return kDifOk; |
| } |
| |
| dif_result_t dif_otbn_imem_read(const dif_otbn_t *otbn, uint32_t offset_bytes, |
| void *dest, size_t len_bytes) { |
| if (otbn == NULL || dest == NULL || |
| !check_offset_len(offset_bytes, len_bytes, OTBN_IMEM_SIZE_BYTES)) { |
| return kDifBadArg; |
| } |
| |
| mmio_region_memcpy_from_mmio32( |
| otbn->base_addr, OTBN_IMEM_REG_OFFSET + offset_bytes, dest, len_bytes); |
| |
| return kDifOk; |
| } |
| |
| dif_result_t dif_otbn_dmem_write(const dif_otbn_t *otbn, uint32_t offset_bytes, |
| const void *src, size_t len_bytes) { |
| if (otbn == NULL || src == NULL || |
| !check_offset_len(offset_bytes, len_bytes, OTBN_DMEM_SIZE_BYTES)) { |
| return kDifBadArg; |
| } |
| |
| mmio_region_memcpy_to_mmio32( |
| otbn->base_addr, OTBN_DMEM_REG_OFFSET + offset_bytes, src, len_bytes); |
| |
| return kDifOk; |
| } |
| |
| dif_result_t dif_otbn_dmem_read(const dif_otbn_t *otbn, uint32_t offset_bytes, |
| void *dest, size_t len_bytes) { |
| if (otbn == NULL || dest == NULL || |
| !check_offset_len(offset_bytes, len_bytes, OTBN_IMEM_SIZE_BYTES)) { |
| return kDifBadArg; |
| } |
| |
| mmio_region_memcpy_from_mmio32( |
| otbn->base_addr, OTBN_DMEM_REG_OFFSET + offset_bytes, dest, len_bytes); |
| |
| return kDifOk; |
| } |
| |
| dif_result_t dif_otbn_set_ctrl_software_errs_fatal(const dif_otbn_t *otbn, |
| bool enable) { |
| if (otbn == NULL) { |
| return kDifBadArg; |
| } |
| |
| // 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; |
| } |
| |
| mmio_region_write32(otbn->base_addr, OTBN_CTRL_REG_OFFSET, new_ctrl); |
| if (mmio_region_read32(otbn->base_addr, OTBN_CTRL_REG_OFFSET) != new_ctrl) { |
| return kDifUnavailable; |
| } |
| |
| return kDifOk; |
| } |
| |
| size_t dif_otbn_get_dmem_size_bytes(const dif_otbn_t *otbn) { |
| return OTBN_DMEM_SIZE_BYTES; |
| } |
| |
| size_t dif_otbn_get_imem_size_bytes(const dif_otbn_t *otbn) { |
| return OTBN_IMEM_SIZE_BYTES; |
| } |