blob: b840c3736a13ed2f04faf496c3880c9b2bc411bd [file] [log] [blame]
// 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_DMEM_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;
}