blob: 90afed80fc88073cbff6fd41b5a11311b453c1d7 [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_entropy_src.h"
#include <stddef.h>
#include "sw/device/lib/base/memory.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/dif/dif_base.h"
#include "entropy_src_regs.h" // Generated.
/**
* Sets the `entropy` source configuration register with the settings
* derived from `config`.
*/
static void config_register_set(const dif_entropy_src_t *entropy_src,
const dif_entropy_src_config_t *config) {
// TODO: Make this configurable at the API level.
uint32_t reg = bitfield_field32_write(
0, ENTROPY_SRC_CONF_BOOT_BYPASS_DISABLE_FIELD, 0x5);
uint32_t health_clr_sel = config->reset_health_test_registers ? 0xa : 0x5;
reg = bitfield_field32_write(reg, ENTROPY_SRC_CONF_HEALTH_TEST_CLR_FIELD,
health_clr_sel);
reg = bitfield_field32_write(reg,
ENTROPY_SRC_CONF_ENTROPY_DATA_REG_ENABLE_FIELD,
config->route_to_firmware ? 0xa : 0x5);
// Configure single RNG bit mode
uint32_t rng_bit_en =
(config->single_bit_mode == kDifEntropySrcSingleBitModeDisabled) ? 0x5
: 0xa;
reg = bitfield_field32_write(reg, ENTROPY_SRC_CONF_RNG_BIT_ENABLE_FIELD,
rng_bit_en);
uint32_t rng_bit_sel = rng_bit_en ? config->single_bit_mode : 0;
reg = bitfield_field32_write(reg, ENTROPY_SRC_CONF_RNG_BIT_SEL_FIELD,
rng_bit_sel);
uint32_t sw_rd_en = config->route_to_firmware ? 0xa : 0x5;
reg = bitfield_field32_write(
reg, ENTROPY_SRC_CONF_ENTROPY_DATA_REG_ENABLE_FIELD, sw_rd_en);
// Enable configuration
uint32_t enable_val = config->mode != kDifEntropySrcModeDisabled ? 0xa : 0x5;
reg = bitfield_field32_write(reg, ENTROPY_SRC_CONF_ENABLE_FIELD, enable_val);
mmio_region_write32(entropy_src->base_addr, ENTROPY_SRC_CONF_REG_OFFSET, reg);
}
/**
* Sets the `entropy` source firmware override register with the settings
* derived from `config`.
*/
static dif_result_t fw_override_set(
const dif_entropy_src_t *entropy_src,
const dif_entropy_src_fw_override_config_t *config) {
if (config->buffer_threshold > kDifEntropySrcFifoMaxCapacity) {
return kDifBadArg;
}
if (config->entropy_insert_enable && !config->enable) {
return kDifBadArg;
}
mmio_region_write32(entropy_src->base_addr,
ENTROPY_SRC_OBSERVE_FIFO_THRESH_REG_OFFSET,
config->buffer_threshold);
uint32_t reg =
bitfield_field32_write(0, ENTROPY_SRC_FW_OV_CONTROL_FW_OV_MODE_FIELD,
config->enable ? 0xa : 0x5);
reg = bitfield_field32_write(
reg, ENTROPY_SRC_FW_OV_CONTROL_FW_OV_ENTROPY_INSERT_FIELD,
config->entropy_insert_enable ? 0xa : 0x5);
mmio_region_write32(entropy_src->base_addr,
ENTROPY_SRC_FW_OV_CONTROL_REG_OFFSET, reg);
return kDifOk;
}
dif_result_t dif_entropy_src_init(mmio_region_t base_addr,
dif_entropy_src_t *entropy_src) {
if (entropy_src == NULL) {
return kDifBadArg;
}
entropy_src->base_addr = base_addr;
return kDifOk;
}
dif_result_t dif_entropy_src_configure(const dif_entropy_src_t *entropy_src,
dif_entropy_src_config_t config) {
if (entropy_src == NULL) {
return kDifBadArg;
}
dif_result_t result = fw_override_set(entropy_src, &config.fw_override);
if (result != kDifOk) {
return result;
}
// TODO: Add test configuration parameters.
// Conditioning bypass is hardcoded to disabled. Conditioning bypass is not
// intended as a regular mode of operation.
uint32_t es_route_val = config.route_to_firmware ? 0xa : 0x5;
uint32_t reg = bitfield_field32_write(
0, ENTROPY_SRC_ENTROPY_CONTROL_ES_ROUTE_FIELD, es_route_val);
reg = bitfield_field32_write(reg, ENTROPY_SRC_ENTROPY_CONTROL_ES_TYPE_FIELD,
0x5);
mmio_region_write32(entropy_src->base_addr,
ENTROPY_SRC_ENTROPY_CONTROL_REG_OFFSET, reg);
config_register_set(entropy_src, &config);
return kDifOk;
}
static bool get_entropy_avail(const dif_entropy_src_t *entropy_src) {
return mmio_region_get_bit32(entropy_src->base_addr,
ENTROPY_SRC_INTR_STATE_REG_OFFSET,
ENTROPY_SRC_INTR_STATE_ES_ENTROPY_VALID_BIT);
}
dif_result_t dif_entropy_src_avail(const dif_entropy_src_t *entropy_src) {
if (entropy_src == NULL) {
return kDifBadArg;
}
return get_entropy_avail(entropy_src) ? kDifOk : kDifUnavailable;
}
dif_result_t dif_entropy_src_read(const dif_entropy_src_t *entropy_src,
uint32_t *word) {
if (entropy_src == NULL || word == NULL) {
return kDifBadArg;
}
// Check if entropy is available
if (!get_entropy_avail(entropy_src)) {
return kDifUnavailable;
}
*word = mmio_region_read32(entropy_src->base_addr,
ENTROPY_SRC_ENTROPY_DATA_REG_OFFSET);
// clear interrupt state after fetching read
// if there is still entropy available, the interrupt state will set again
mmio_region_nonatomic_set_bit32(entropy_src->base_addr,
ENTROPY_SRC_INTR_STATE_REG_OFFSET,
ENTROPY_SRC_INTR_STATE_ES_ENTROPY_VALID_BIT);
return kDifOk;
}
dif_result_t dif_entropy_src_fifo_read(const dif_entropy_src_t *entropy_src,
uint32_t *buf, size_t len) {
if (entropy_src == NULL) {
return kDifBadArg;
}
uint32_t reg = mmio_region_read32(entropy_src->base_addr,
ENTROPY_SRC_FW_OV_CONTROL_REG_OFFSET);
if (bitfield_field32_read(reg, ENTROPY_SRC_FW_OV_CONTROL_FW_OV_MODE_FIELD) !=
0xa) {
return kDifError;
}
reg = mmio_region_read32(entropy_src->base_addr,
ENTROPY_SRC_OBSERVE_FIFO_THRESH_REG_OFFSET);
if (reg < len) {
return kDifBadArg;
}
do {
reg = mmio_region_read32(entropy_src->base_addr,
ENTROPY_SRC_INTR_STATE_REG_OFFSET);
} while (!bitfield_bit32_read(
reg, ENTROPY_SRC_INTR_STATE_ES_OBSERVE_FIFO_READY_BIT));
for (size_t i = 0; i < len; ++i) {
reg = mmio_region_read32(entropy_src->base_addr,
ENTROPY_SRC_FW_OV_RD_DATA_REG_OFFSET);
if (buf != NULL) {
buf[i] = reg;
}
}
// Clear the status bit.
reg = bitfield_bit32_write(
0, ENTROPY_SRC_INTR_STATE_ES_OBSERVE_FIFO_READY_BIT, true);
mmio_region_write32(entropy_src->base_addr, ENTROPY_SRC_INTR_STATE_REG_OFFSET,
reg);
return kDifOk;
}
dif_result_t dif_entropy_src_fifo_write(const dif_entropy_src_t *entropy_src,
const uint32_t *buf, size_t len) {
if (entropy_src == NULL || buf == NULL) {
return kDifBadArg;
}
uint32_t reg = mmio_region_read32(entropy_src->base_addr,
ENTROPY_SRC_FW_OV_CONTROL_REG_OFFSET);
if (bitfield_field32_read(reg, ENTROPY_SRC_FW_OV_CONTROL_FW_OV_MODE_FIELD) !=
0xa ||
bitfield_field32_read(
reg, ENTROPY_SRC_FW_OV_CONTROL_FW_OV_ENTROPY_INSERT_FIELD) != 0xa) {
return kDifBadArg;
}
for (size_t i = 0; i < len; ++i) {
mmio_region_write32(entropy_src->base_addr,
ENTROPY_SRC_FW_OV_WR_DATA_REG_OFFSET, buf[i]);
}
return kDifOk;
}
dif_result_t dif_entropy_src_disable(const dif_entropy_src_t *entropy_src) {
if (entropy_src == NULL) {
return kDifBadArg;
}
// TODO: should first check if entropy is locked and return error if it is.
mmio_region_write32(entropy_src->base_addr, ENTROPY_SRC_CONF_REG_OFFSET, 0);
const dif_entropy_src_fw_override_config_t kDefaultFwOverrideConfig = {
.enable = false,
.entropy_insert_enable = false,
.buffer_threshold = kDifEntropyFifoIntDefaultThreshold,
};
return fw_override_set(entropy_src, &kDefaultFwOverrideConfig);
}