blob: aa04c0f3f95d4f54b1e49af736dbb5281925b7fb [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/silicon_creator/lib/drivers/rnd.h"
#include "sw/device/lib/base/abs_mmio.h"
#include "sw/device/lib/base/csr.h"
#include "sw/device/lib/base/hardened.h"
#include "sw/device/lib/base/macros.h"
#include "sw/device/silicon_creator/lib/crc32.h"
#include "sw/device/silicon_creator/lib/drivers/otp.h"
#include "entropy_src_regs.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
#include "otp_ctrl_regs.h"
#include "rv_core_ibex_regs.h"
enum {
kBaseEntropySrc = TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR,
kBaseIbex = TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR,
// This covers the health threshold registers which are contiguous. The alert
// threshold register is not included here.
kNumHealthRegisters = 9,
};
// Check the number of health registers covered by this driver.
static_assert(kNumHealthRegisters ==
(ENTROPY_SRC_EXTHT_LO_THRESHOLDS_REG_OFFSET -
ENTROPY_SRC_REPCNT_THRESHOLDS_REG_OFFSET) /
sizeof(uint32_t) +
1,
"Unexpected entropy_src health register count.");
// Ensure the relative offsets of OTP versus entropy_src registers are
// equivalent. This is imporant as rom_start.S uses a copy function to
// copy the values from OTP into the entropy_src.
#define ASSERT_REG_OFFSET(otp_offset_, entropy_src_offset_) \
static_assert( \
((otp_offset_)-OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_REPCNT_THRESHOLDS_OFFSET) == \
((entropy_src_offset_)-ENTROPY_SRC_REPCNT_THRESHOLDS_REG_OFFSET), \
"OTP configuration offset does not match the expected entropy_src " \
"register offset")
ASSERT_REG_OFFSET(OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_REPCNT_THRESHOLDS_OFFSET,
ENTROPY_SRC_REPCNT_THRESHOLDS_REG_OFFSET);
ASSERT_REG_OFFSET(OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_REPCNTS_THRESHOLDS_OFFSET,
ENTROPY_SRC_REPCNTS_THRESHOLDS_REG_OFFSET);
ASSERT_REG_OFFSET(OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_ADAPTP_HI_THRESHOLDS_OFFSET,
ENTROPY_SRC_ADAPTP_HI_THRESHOLDS_REG_OFFSET);
ASSERT_REG_OFFSET(OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_ADAPTP_LO_THRESHOLDS_OFFSET,
ENTROPY_SRC_ADAPTP_LO_THRESHOLDS_REG_OFFSET);
ASSERT_REG_OFFSET(OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_BUCKET_THRESHOLDS_OFFSET,
ENTROPY_SRC_BUCKET_THRESHOLDS_REG_OFFSET);
ASSERT_REG_OFFSET(OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_MARKOV_HI_THRESHOLDS_OFFSET,
ENTROPY_SRC_MARKOV_HI_THRESHOLDS_REG_OFFSET);
ASSERT_REG_OFFSET(OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_MARKOV_LO_THRESHOLDS_OFFSET,
ENTROPY_SRC_MARKOV_LO_THRESHOLDS_REG_OFFSET);
ASSERT_REG_OFFSET(OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_EXTHT_HI_THRESHOLDS_OFFSET,
ENTROPY_SRC_EXTHT_HI_THRESHOLDS_REG_OFFSET);
ASSERT_REG_OFFSET(OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_EXTHT_LO_THRESHOLDS_OFFSET,
ENTROPY_SRC_EXTHT_LO_THRESHOLDS_REG_OFFSET);
/**
* Calculates CRC32 over the entropy_src health test and alert thresholds.
*/
uint32_t health_config_crc32(void) {
uint32_t ctx;
crc32_init(&ctx);
// Health test thresholds, whose offsets are statically checked.
uint32_t offset = ENTROPY_SRC_REPCNT_THRESHOLDS_REG_OFFSET;
for (size_t i = 0; i < kNumHealthRegisters; ++i, offset += sizeof(uint32_t)) {
crc32_add32(&ctx, abs_mmio_read32(kBaseEntropySrc + offset));
}
crc32_add32(&ctx, abs_mmio_read32(kBaseEntropySrc +
ENTROPY_SRC_ALERT_THRESHOLD_REG_OFFSET));
return crc32_finish(&ctx);
}
rom_error_t rnd_health_config_check(lifecycle_state_t lc_state) {
if (otp_read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_EN_OFFSET) !=
kHardenedBoolTrue) {
return kErrorOk;
}
uint32_t crc32 = health_config_crc32();
rom_error_t res = crc32;
if (launder32(lc_state) == kLcStateTest) {
res ^= crc32 ^ kErrorOk;
HARDENED_CHECK_EQ(res, kErrorOk);
HARDENED_CHECK_EQ(lc_state, kLcStateTest);
return res;
}
res ^=
otp_read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_HEALTH_CONFIG_DIGEST_OFFSET);
if (launder32(res) != kErrorOk) {
return kErrorRndBadCrc32;
}
HARDENED_CHECK_EQ(res, kErrorOk);
return res;
}
uint32_t rnd_uint32(void) {
if (otp_read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_EN_OFFSET) ==
kHardenedBoolTrue) {
// When bit-0 is clear an EDN request for new data for RND_DATA is
// pending.
while (!abs_mmio_read32(kBaseIbex + RV_CORE_IBEX_RND_STATUS_REG_OFFSET)) {
}
}
uint32_t mcycle;
CSR_READ(CSR_REG_MCYCLE, &mcycle);
return mcycle + abs_mmio_read32(kBaseIbex + RV_CORE_IBEX_RND_DATA_REG_OFFSET);
}