| // 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/bitfield.h" | |
| #include "sw/device/lib/base/memory.h" | |
| #include "sw/device/lib/base/mmio.h" | |
| #include "sw/device/lib/base/multibits.h" | |
| #include "sw/device/lib/dif/dif_base.h" | |
| #include "entropy_src_regs.h" // Generated. | |
| dif_result_t dif_entropy_src_stop(const dif_entropy_src_t *entropy_src) { | |
| if (entropy_src == NULL) { | |
| return kDifBadArg; | |
| } | |
| mmio_region_write32(entropy_src->base_addr, | |
| ENTROPY_SRC_MODULE_ENABLE_REG_OFFSET, | |
| ENTROPY_SRC_MODULE_ENABLE_REG_RESVAL); | |
| // once disabled, the entropy_src regwen is released | |
| if (!mmio_region_read32(entropy_src->base_addr, | |
| ENTROPY_SRC_REGWEN_REG_OFFSET)) { | |
| return kDifLocked; | |
| } | |
| // set back to default value | |
| mmio_region_write32(entropy_src->base_addr, | |
| ENTROPY_SRC_ENTROPY_CONTROL_REG_OFFSET, | |
| ENTROPY_SRC_ENTROPY_CONTROL_REG_RESVAL); | |
| mmio_region_write32(entropy_src->base_addr, ENTROPY_SRC_CONF_REG_OFFSET, | |
| ENTROPY_SRC_CONF_REG_RESVAL); | |
| mmio_region_write32(entropy_src->base_addr, | |
| ENTROPY_SRC_HEALTH_TEST_WINDOWS_REG_OFFSET, | |
| ENTROPY_SRC_HEALTH_TEST_WINDOWS_REG_RESVAL); | |
| mmio_region_write32(entropy_src->base_addr, | |
| ENTROPY_SRC_ALERT_THRESHOLD_REG_OFFSET, | |
| ENTROPY_SRC_ALERT_THRESHOLD_REG_RESVAL); | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_configure(const dif_entropy_src_t *entropy_src, | |
| dif_entropy_src_config_t config, | |
| dif_toggle_t enabled) { | |
| if (entropy_src == NULL || | |
| config.single_bit_mode > kDifEntropySrcSingleBitModeDisabled || | |
| !dif_is_valid_toggle(enabled) || config.health_test_window_size == 0) { | |
| return kDifBadArg; | |
| } | |
| if (!mmio_region_read32(entropy_src->base_addr, | |
| ENTROPY_SRC_REGWEN_REG_OFFSET)) { | |
| return kDifLocked; | |
| } | |
| // ENTROPY_CONTROL register configuration. | |
| uint32_t entropy_ctrl_reg = bitfield_field32_write( | |
| 0, ENTROPY_SRC_ENTROPY_CONTROL_ES_ROUTE_FIELD, | |
| config.route_to_firmware ? kMultiBitBool4True : kMultiBitBool4False); | |
| entropy_ctrl_reg = bitfield_field32_write( | |
| entropy_ctrl_reg, ENTROPY_SRC_ENTROPY_CONTROL_ES_TYPE_FIELD, | |
| config.bypass_conditioner ? kMultiBitBool4True : kMultiBitBool4False); | |
| mmio_region_write32(entropy_src->base_addr, | |
| ENTROPY_SRC_ENTROPY_CONTROL_REG_OFFSET, entropy_ctrl_reg); | |
| // CONF register configuration. | |
| // Configure FIPS enable. | |
| // TODO: Add additional DIF to toggle this bit independently. | |
| uint32_t entropy_conf_reg = bitfield_field32_write( | |
| 0, ENTROPY_SRC_CONF_FIPS_ENABLE_FIELD, | |
| config.fips_enable ? kMultiBitBool4True : kMultiBitBool4False); | |
| // Configure entropy data register enable (enables firmware to read entropy). | |
| entropy_conf_reg = bitfield_field32_write( | |
| entropy_conf_reg, ENTROPY_SRC_CONF_ENTROPY_DATA_REG_ENABLE_FIELD, | |
| config.route_to_firmware ? kMultiBitBool4True : kMultiBitBool4False); | |
| // Configure the health test threshold scope. | |
| entropy_conf_reg = bitfield_field32_write( | |
| entropy_conf_reg, ENTROPY_SRC_CONF_THRESHOLD_SCOPE_FIELD, | |
| config.health_test_threshold_scope ? kMultiBitBool4True | |
| : kMultiBitBool4False); | |
| // Configure single RNG bit mode. | |
| uint32_t rng_bit_en = | |
| (config.single_bit_mode == kDifEntropySrcSingleBitModeDisabled) | |
| ? kMultiBitBool4False | |
| : kMultiBitBool4True; | |
| entropy_conf_reg = bitfield_field32_write( | |
| entropy_conf_reg, ENTROPY_SRC_CONF_RNG_BIT_ENABLE_FIELD, rng_bit_en); | |
| uint32_t rng_bit_sel = | |
| (rng_bit_en == kMultiBitBool4True) ? config.single_bit_mode : 0; | |
| entropy_conf_reg = bitfield_field32_write( | |
| entropy_conf_reg, ENTROPY_SRC_CONF_RNG_BIT_SEL_FIELD, rng_bit_sel); | |
| uint32_t sw_rd_en = | |
| config.route_to_firmware ? kMultiBitBool4True : kMultiBitBool4False; | |
| entropy_conf_reg = bitfield_field32_write( | |
| entropy_conf_reg, ENTROPY_SRC_CONF_ENTROPY_DATA_REG_ENABLE_FIELD, | |
| sw_rd_en); | |
| mmio_region_write32(entropy_src->base_addr, ENTROPY_SRC_CONF_REG_OFFSET, | |
| entropy_conf_reg); | |
| // Configure health test window. | |
| // Conditioning bypass is hardcoded to disabled (see above). If we want to | |
| // expose the ES_TYPE field in the future, we need to also configure the | |
| // health test window size for bypass mode. Note however that the only | |
| // supported bypass window size is the default value of 0x60 at the moment, | |
| // hence even if the ES_TYPE field is exposed in the future, we should not | |
| // change the bypass window size as this may break. | |
| uint32_t health_test_window_sizes = | |
| bitfield_field32_write(ENTROPY_SRC_HEALTH_TEST_WINDOWS_REG_RESVAL, | |
| ENTROPY_SRC_HEALTH_TEST_WINDOWS_FIPS_WINDOW_FIELD, | |
| config.health_test_window_size); | |
| mmio_region_write32(entropy_src->base_addr, | |
| ENTROPY_SRC_HEALTH_TEST_WINDOWS_REG_OFFSET, | |
| health_test_window_sizes); | |
| // Alert Threshold register configuration. | |
| uint32_t alert_threshold = bitfield_field32_write( | |
| 0, ENTROPY_SRC_ALERT_THRESHOLD_ALERT_THRESHOLD_FIELD, | |
| config.alert_threshold); | |
| alert_threshold = bitfield_field32_write( | |
| alert_threshold, ENTROPY_SRC_ALERT_THRESHOLD_ALERT_THRESHOLD_INV_FIELD, | |
| ~config.alert_threshold); | |
| mmio_region_write32(entropy_src->base_addr, | |
| ENTROPY_SRC_ALERT_THRESHOLD_REG_OFFSET, alert_threshold); | |
| // MODULE_ENABLE register configuration. | |
| mmio_region_write32(entropy_src->base_addr, | |
| ENTROPY_SRC_MODULE_ENABLE_REG_OFFSET, | |
| dif_toggle_to_multi_bit_bool4(enabled)); | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_fw_override_configure( | |
| const dif_entropy_src_t *entropy_src, | |
| dif_entropy_src_fw_override_config_t config, dif_toggle_t enabled) { | |
| if (entropy_src == NULL || | |
| config.buffer_threshold > | |
| ENTROPY_SRC_OBSERVE_FIFO_THRESH_OBSERVE_FIFO_THRESH_MASK || | |
| config.buffer_threshold == 0 || !dif_is_valid_toggle(enabled)) { | |
| return kDifBadArg; | |
| } | |
| if (!mmio_region_read32(entropy_src->base_addr, | |
| ENTROPY_SRC_REGWEN_REG_OFFSET)) { | |
| return kDifLocked; | |
| } | |
| 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, | |
| dif_toggle_to_multi_bit_bool4(enabled)); | |
| reg = bitfield_field32_write( | |
| reg, ENTROPY_SRC_FW_OV_CONTROL_FW_OV_ENTROPY_INSERT_FIELD, | |
| config.entropy_insert_enable ? kMultiBitBool4True : kMultiBitBool4False); | |
| mmio_region_write32(entropy_src->base_addr, | |
| ENTROPY_SRC_FW_OV_CONTROL_REG_OFFSET, reg); | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_health_test_configure( | |
| const dif_entropy_src_t *entropy_src, | |
| dif_entropy_src_health_test_config_t config) { | |
| if (entropy_src == NULL) { | |
| return kDifBadArg; | |
| } | |
| if (!mmio_region_read32(entropy_src->base_addr, | |
| ENTROPY_SRC_REGWEN_REG_OFFSET)) { | |
| return kDifLocked; | |
| } | |
| ptrdiff_t high_thresholds_reg_offset = -1; | |
| ptrdiff_t low_thresholds_reg_offset = -1; | |
| switch (config.test_type) { | |
| case kDifEntropySrcTestRepetitionCount: | |
| high_thresholds_reg_offset = ENTROPY_SRC_REPCNT_THRESHOLDS_REG_OFFSET; | |
| // Ensure low threshold is zero. There is no low threshold for this test. | |
| if (config.low_threshold) { | |
| return kDifBadArg; | |
| } | |
| break; | |
| case kDifEntropySrcTestRepetitionCountSymbol: | |
| high_thresholds_reg_offset = ENTROPY_SRC_REPCNTS_THRESHOLDS_REG_OFFSET; | |
| // Ensure low threshold is zero. There is no low threshold for this test. | |
| if (config.low_threshold) { | |
| return kDifBadArg; | |
| } | |
| break; | |
| case kDifEntropySrcTestAdaptiveProportion: | |
| high_thresholds_reg_offset = ENTROPY_SRC_ADAPTP_HI_THRESHOLDS_REG_OFFSET; | |
| low_thresholds_reg_offset = ENTROPY_SRC_ADAPTP_LO_THRESHOLDS_REG_OFFSET; | |
| break; | |
| case kDifEntropySrcTestBucket: | |
| high_thresholds_reg_offset = ENTROPY_SRC_BUCKET_THRESHOLDS_REG_OFFSET; | |
| // Ensure low threshold is zero. There is no low threshold for this test. | |
| if (config.low_threshold) { | |
| return kDifBadArg; | |
| } | |
| break; | |
| case kDifEntropySrcTestMarkov: | |
| high_thresholds_reg_offset = ENTROPY_SRC_MARKOV_HI_THRESHOLDS_REG_OFFSET; | |
| low_thresholds_reg_offset = ENTROPY_SRC_MARKOV_LO_THRESHOLDS_REG_OFFSET; | |
| break; | |
| case kDifEntropySrcTestMailbox: | |
| high_thresholds_reg_offset = ENTROPY_SRC_EXTHT_HI_THRESHOLDS_REG_OFFSET; | |
| low_thresholds_reg_offset = ENTROPY_SRC_EXTHT_LO_THRESHOLDS_REG_OFFSET; | |
| break; | |
| default: | |
| return kDifBadArg; | |
| } | |
| // Conditioning bypass is hardcoded to disabled (see above). Therefore we only | |
| // configure FIPS mode thresholds. | |
| mmio_region_write32(entropy_src->base_addr, high_thresholds_reg_offset, | |
| config.high_threshold); | |
| if (low_thresholds_reg_offset != -1) { | |
| mmio_region_write32(entropy_src->base_addr, low_thresholds_reg_offset, | |
| config.low_threshold); | |
| } | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_set_enabled(const dif_entropy_src_t *entropy_src, | |
| dif_toggle_t enabled) { | |
| if (entropy_src == NULL || !dif_is_valid_toggle(enabled)) { | |
| return kDifBadArg; | |
| } | |
| if (!mmio_region_read32(entropy_src->base_addr, | |
| ENTROPY_SRC_ME_REGWEN_REG_OFFSET)) { | |
| return kDifLocked; | |
| } | |
| mmio_region_write32(entropy_src->base_addr, | |
| ENTROPY_SRC_MODULE_ENABLE_REG_OFFSET, | |
| dif_toggle_to_multi_bit_bool4(enabled)); | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_lock(const dif_entropy_src_t *entropy_src) { | |
| if (entropy_src == NULL) { | |
| return kDifBadArg; | |
| } | |
| mmio_region_write32(entropy_src->base_addr, ENTROPY_SRC_ME_REGWEN_REG_OFFSET, | |
| 0); | |
| mmio_region_write32(entropy_src->base_addr, ENTROPY_SRC_SW_REGUPD_REG_OFFSET, | |
| 0); | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_is_locked(const dif_entropy_src_t *entropy_src, | |
| bool *is_locked) { | |
| if (entropy_src == NULL || is_locked == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t module_enable_regwen = mmio_region_read32( | |
| entropy_src->base_addr, ENTROPY_SRC_ME_REGWEN_REG_OFFSET); | |
| uint32_t sw_regupd = mmio_region_read32(entropy_src->base_addr, | |
| ENTROPY_SRC_SW_REGUPD_REG_OFFSET); | |
| if (module_enable_regwen == sw_regupd) { | |
| *is_locked = sw_regupd == 0; | |
| } else { | |
| // Since we actuate these together, either both should be 0 (locked), or | |
| // both should be 1 (unlocked). If only one is locked, then we have | |
| // gotten into a bad state. | |
| return kDifError; | |
| } | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_get_health_test_stats( | |
| const dif_entropy_src_t *entropy_src, | |
| dif_entropy_src_health_test_stats_t *stats) { | |
| if (entropy_src == NULL || stats == NULL) { | |
| return kDifBadArg; | |
| } | |
| ptrdiff_t high_watermarks_reg_offset = -1; | |
| ptrdiff_t low_watermarks_reg_offset = -1; | |
| ptrdiff_t high_fails_reg_offset = -1; | |
| ptrdiff_t low_fails_reg_offset = -1; | |
| for (uint32_t i = 0; i < kDifEntropySrcTestNumVariants; ++i) { | |
| switch ((dif_entropy_src_test_t)i) { | |
| case kDifEntropySrcTestRepetitionCount: | |
| high_watermarks_reg_offset = | |
| ENTROPY_SRC_REPCNT_HI_WATERMARKS_REG_OFFSET; | |
| low_watermarks_reg_offset = -1; | |
| high_fails_reg_offset = ENTROPY_SRC_REPCNT_TOTAL_FAILS_REG_OFFSET; | |
| low_fails_reg_offset = -1; | |
| break; | |
| case kDifEntropySrcTestRepetitionCountSymbol: | |
| high_watermarks_reg_offset = | |
| ENTROPY_SRC_REPCNTS_HI_WATERMARKS_REG_OFFSET; | |
| low_watermarks_reg_offset = -1; | |
| high_fails_reg_offset = ENTROPY_SRC_REPCNTS_TOTAL_FAILS_REG_OFFSET; | |
| low_fails_reg_offset = -1; | |
| break; | |
| case kDifEntropySrcTestAdaptiveProportion: | |
| high_watermarks_reg_offset = | |
| ENTROPY_SRC_ADAPTP_HI_WATERMARKS_REG_OFFSET; | |
| low_watermarks_reg_offset = ENTROPY_SRC_ADAPTP_LO_WATERMARKS_REG_OFFSET; | |
| high_fails_reg_offset = ENTROPY_SRC_ADAPTP_HI_TOTAL_FAILS_REG_OFFSET; | |
| low_fails_reg_offset = ENTROPY_SRC_ADAPTP_LO_TOTAL_FAILS_REG_OFFSET; | |
| break; | |
| case kDifEntropySrcTestBucket: | |
| high_watermarks_reg_offset = | |
| ENTROPY_SRC_BUCKET_HI_WATERMARKS_REG_OFFSET; | |
| low_watermarks_reg_offset = -1; | |
| high_fails_reg_offset = ENTROPY_SRC_BUCKET_TOTAL_FAILS_REG_OFFSET; | |
| low_fails_reg_offset = -1; | |
| break; | |
| case kDifEntropySrcTestMarkov: | |
| high_watermarks_reg_offset = | |
| ENTROPY_SRC_MARKOV_HI_WATERMARKS_REG_OFFSET; | |
| low_watermarks_reg_offset = ENTROPY_SRC_MARKOV_LO_WATERMARKS_REG_OFFSET; | |
| high_fails_reg_offset = ENTROPY_SRC_MARKOV_HI_TOTAL_FAILS_REG_OFFSET; | |
| low_fails_reg_offset = ENTROPY_SRC_MARKOV_LO_TOTAL_FAILS_REG_OFFSET; | |
| break; | |
| case kDifEntropySrcTestMailbox: | |
| high_watermarks_reg_offset = ENTROPY_SRC_EXTHT_HI_WATERMARKS_REG_OFFSET; | |
| low_watermarks_reg_offset = ENTROPY_SRC_EXTHT_LO_WATERMARKS_REG_OFFSET; | |
| high_fails_reg_offset = ENTROPY_SRC_EXTHT_HI_TOTAL_FAILS_REG_OFFSET; | |
| low_fails_reg_offset = ENTROPY_SRC_EXTHT_LO_TOTAL_FAILS_REG_OFFSET; | |
| break; | |
| default: | |
| return kDifError; | |
| } | |
| // Conditioning bypass is hardcoded to disabled (see above). Therefore we | |
| // only read FIPS mode stats. | |
| stats->high_watermark[i] = | |
| mmio_region_read32(entropy_src->base_addr, high_watermarks_reg_offset); | |
| stats->low_watermark[i] = | |
| low_watermarks_reg_offset == -1 | |
| ? 0 | |
| : mmio_region_read32(entropy_src->base_addr, | |
| low_watermarks_reg_offset); | |
| stats->high_fails[i] = | |
| mmio_region_read32(entropy_src->base_addr, high_fails_reg_offset); | |
| stats->low_fails[i] = | |
| low_fails_reg_offset == -1 | |
| ? 0 | |
| : mmio_region_read32(entropy_src->base_addr, low_fails_reg_offset); | |
| } | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_get_alert_fail_counts( | |
| const dif_entropy_src_t *entropy_src, | |
| dif_entropy_src_alert_fail_counts_t *counts) { | |
| if (entropy_src == NULL || counts == NULL) { | |
| return kDifBadArg; | |
| } | |
| counts->total_fails = mmio_region_read32( | |
| entropy_src->base_addr, ENTROPY_SRC_ALERT_SUMMARY_FAIL_COUNTS_REG_OFFSET); | |
| uint32_t alert_fail_counts = mmio_region_read32( | |
| entropy_src->base_addr, ENTROPY_SRC_ALERT_FAIL_COUNTS_REG_OFFSET); | |
| uint32_t extht_alert_fail_counts = mmio_region_read32( | |
| entropy_src->base_addr, ENTROPY_SRC_EXTHT_FAIL_COUNTS_REG_OFFSET); | |
| // Unpack high threshold failure counts. | |
| counts->high_fails[kDifEntropySrcTestRepetitionCount] = bitfield_field32_read( | |
| alert_fail_counts, ENTROPY_SRC_ALERT_FAIL_COUNTS_REPCNT_FAIL_COUNT_FIELD); | |
| counts->high_fails[kDifEntropySrcTestRepetitionCountSymbol] = | |
| bitfield_field32_read( | |
| alert_fail_counts, | |
| ENTROPY_SRC_ALERT_FAIL_COUNTS_REPCNTS_FAIL_COUNT_FIELD); | |
| counts->high_fails[kDifEntropySrcTestAdaptiveProportion] = | |
| bitfield_field32_read( | |
| alert_fail_counts, | |
| ENTROPY_SRC_ALERT_FAIL_COUNTS_ADAPTP_HI_FAIL_COUNT_FIELD); | |
| counts->high_fails[kDifEntropySrcTestBucket] = bitfield_field32_read( | |
| alert_fail_counts, ENTROPY_SRC_ALERT_FAIL_COUNTS_BUCKET_FAIL_COUNT_FIELD); | |
| counts->high_fails[kDifEntropySrcTestMarkov] = bitfield_field32_read( | |
| alert_fail_counts, | |
| ENTROPY_SRC_ALERT_FAIL_COUNTS_MARKOV_HI_FAIL_COUNT_FIELD); | |
| counts->high_fails[kDifEntropySrcTestMailbox] = bitfield_field32_read( | |
| extht_alert_fail_counts, | |
| ENTROPY_SRC_EXTHT_FAIL_COUNTS_EXTHT_HI_FAIL_COUNT_FIELD); | |
| // Unpack low threshold failure counts. | |
| counts->low_fails[kDifEntropySrcTestRepetitionCount] = 0; | |
| counts->low_fails[kDifEntropySrcTestRepetitionCountSymbol] = 0; | |
| counts->low_fails[kDifEntropySrcTestAdaptiveProportion] = | |
| bitfield_field32_read( | |
| alert_fail_counts, | |
| ENTROPY_SRC_ALERT_FAIL_COUNTS_ADAPTP_LO_FAIL_COUNT_FIELD); | |
| counts->low_fails[kDifEntropySrcTestBucket] = 0; | |
| counts->low_fails[kDifEntropySrcTestMarkov] = bitfield_field32_read( | |
| alert_fail_counts, | |
| ENTROPY_SRC_ALERT_FAIL_COUNTS_MARKOV_LO_FAIL_COUNT_FIELD); | |
| counts->low_fails[kDifEntropySrcTestMailbox] = bitfield_field32_read( | |
| extht_alert_fail_counts, | |
| ENTROPY_SRC_EXTHT_FAIL_COUNTS_EXTHT_LO_FAIL_COUNT_FIELD); | |
| return kDifOk; | |
| } | |
| static bool is_entropy_available(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_is_entropy_available( | |
| const dif_entropy_src_t *entropy_src) { | |
| if (entropy_src == NULL) { | |
| return kDifBadArg; | |
| } | |
| return is_entropy_available(entropy_src) ? kDifOk : kDifUnavailable; | |
| } | |
| dif_result_t dif_entropy_src_non_blocking_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 (!is_entropy_available(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_observe_fifo_blocking_read( | |
| const dif_entropy_src_t *entropy_src, uint32_t *buf, size_t len) { | |
| if (entropy_src == NULL) { | |
| return kDifBadArg; | |
| } | |
| // Check that the number of bytes to be read is less than or equal to the FIFO | |
| // threshold that triggers an interrupt. | |
| uint32_t reg = mmio_region_read32(entropy_src->base_addr, | |
| ENTROPY_SRC_OBSERVE_FIFO_THRESH_REG_OFFSET); | |
| if (len > reg) { | |
| return kDifBadArg; | |
| } | |
| // Check that we are in firmware override mode. We can only read from the | |
| // override FIFO if we are. | |
| 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) != | |
| kMultiBitBool4True) { | |
| return kDifError; | |
| } | |
| // Block until there is enough data in the observe FIFO. | |
| 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)); | |
| // Read post-health test, pre-conditioned, entropy from the observe FIFO. | |
| 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_observe_fifo_write( | |
| const dif_entropy_src_t *entropy_src, const uint32_t *buf, size_t len, | |
| size_t *written) { | |
| if (entropy_src == NULL || buf == NULL) { | |
| return kDifBadArg; | |
| } | |
| // Check that we are in firmware override mode and that we can insert data | |
| // into the entropy pipeline. | |
| 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) != | |
| kMultiBitBool4True || | |
| bitfield_field32_read( | |
| reg, ENTROPY_SRC_FW_OV_CONTROL_FW_OV_ENTROPY_INSERT_FIELD) != | |
| kMultiBitBool4True) { | |
| return kDifError; | |
| } | |
| // Check if the FIFO is full before writing each word. | |
| for (size_t i = 0; i < len; ++i) { | |
| if (mmio_region_read32(entropy_src->base_addr, | |
| ENTROPY_SRC_FW_OV_WR_FIFO_FULL_REG_OFFSET)) { | |
| if (written) { | |
| *written = i; | |
| } | |
| return kDifIpFifoFull; | |
| } | |
| mmio_region_write32(entropy_src->base_addr, | |
| ENTROPY_SRC_FW_OV_WR_DATA_REG_OFFSET, buf[i]); | |
| } | |
| if (written) { | |
| *written = len; | |
| } | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_conditioner_start( | |
| const dif_entropy_src_t *entropy_src) { | |
| if (entropy_src == NULL) { | |
| return kDifBadArg; | |
| } | |
| // Check if SHA3 conditioner operation has already started. | |
| uint32_t current_val = mmio_region_read32( | |
| entropy_src->base_addr, ENTROPY_SRC_FW_OV_SHA3_START_REG_OFFSET); | |
| if (current_val == kMultiBitBool4True) { | |
| return kDifUnavailable; | |
| } | |
| mmio_region_write32(entropy_src->base_addr, | |
| ENTROPY_SRC_FW_OV_SHA3_START_REG_OFFSET, | |
| kMultiBitBool4True); | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_conditioner_stop( | |
| const dif_entropy_src_t *entropy_src) { | |
| if (entropy_src == NULL) { | |
| return kDifBadArg; | |
| } | |
| // Check the FW_OV_WR_FIFO_FULL register to determine in any data is | |
| // stalling at the input of the SHA3 conditioner. | |
| if (mmio_region_read32(entropy_src->base_addr, | |
| ENTROPY_SRC_FW_OV_WR_FIFO_FULL_REG_OFFSET)) { | |
| return kDifIpFifoFull; | |
| } | |
| mmio_region_write32(entropy_src->base_addr, | |
| ENTROPY_SRC_FW_OV_SHA3_START_REG_OFFSET, | |
| kMultiBitBool4False); | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_is_fifo_full(const dif_entropy_src_t *entropy_src, | |
| bool *is_full) { | |
| if (entropy_src == NULL || is_full == NULL) { | |
| return kDifBadArg; | |
| } | |
| *is_full = mmio_region_read32(entropy_src->base_addr, | |
| ENTROPY_SRC_FW_OV_WR_FIFO_FULL_REG_OFFSET); | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_has_fifo_overflowed( | |
| const dif_entropy_src_t *entropy_src, bool *has_overflowed) { | |
| if (entropy_src == NULL || has_overflowed == NULL) { | |
| return kDifBadArg; | |
| } | |
| *has_overflowed = mmio_region_read32( | |
| entropy_src->base_addr, ENTROPY_SRC_FW_OV_RD_FIFO_OVERFLOW_REG_OFFSET); | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_clear_fifo_overflow( | |
| const dif_entropy_src_t *entropy_src) { | |
| if (entropy_src == NULL) { | |
| return kDifBadArg; | |
| } | |
| mmio_region_write32(entropy_src->base_addr, | |
| ENTROPY_SRC_FW_OV_RD_FIFO_OVERFLOW_REG_OFFSET, 0); | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_get_fifo_depth( | |
| const dif_entropy_src_t *entropy_src, uint32_t *fifo_depth) { | |
| if (entropy_src == NULL || fifo_depth == NULL) { | |
| return kDifBadArg; | |
| } | |
| *fifo_depth = mmio_region_read32(entropy_src->base_addr, | |
| ENTROPY_SRC_OBSERVE_FIFO_DEPTH_REG_OFFSET); | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_get_debug_state( | |
| const dif_entropy_src_t *entropy_src, | |
| dif_entropy_src_debug_state_t *debug_state) { | |
| if (entropy_src == NULL || debug_state == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t debug_state_reg = mmio_region_read32( | |
| entropy_src->base_addr, ENTROPY_SRC_DEBUG_STATUS_REG_OFFSET); | |
| debug_state->entropy_fifo_depth = bitfield_field32_read( | |
| debug_state_reg, ENTROPY_SRC_DEBUG_STATUS_ENTROPY_FIFO_DEPTH_FIELD); | |
| debug_state->sha3_fsm_state = bitfield_field32_read( | |
| debug_state_reg, ENTROPY_SRC_DEBUG_STATUS_SHA3_FSM_FIELD); | |
| debug_state->sha3_block_processed = bitfield_bit32_read( | |
| debug_state_reg, ENTROPY_SRC_DEBUG_STATUS_SHA3_BLOCK_PR_BIT); | |
| debug_state->sha3_squeezing = bitfield_bit32_read( | |
| debug_state_reg, ENTROPY_SRC_DEBUG_STATUS_SHA3_SQUEEZING_BIT); | |
| debug_state->sha3_absorbed = bitfield_bit32_read( | |
| debug_state_reg, ENTROPY_SRC_DEBUG_STATUS_SHA3_ABSORBED_BIT); | |
| debug_state->sha3_error = bitfield_bit32_read( | |
| debug_state_reg, ENTROPY_SRC_DEBUG_STATUS_SHA3_ERR_BIT); | |
| debug_state->main_fsm_is_idle = bitfield_bit32_read( | |
| debug_state_reg, ENTROPY_SRC_DEBUG_STATUS_MAIN_SM_IDLE_BIT); | |
| debug_state->main_fsm_boot_done = bitfield_bit32_read( | |
| debug_state_reg, ENTROPY_SRC_DEBUG_STATUS_MAIN_SM_BOOT_DONE_BIT); | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_get_recoverable_alerts( | |
| const dif_entropy_src_t *entropy_src, uint32_t *alerts) { | |
| if (entropy_src == NULL || alerts == NULL) { | |
| return kDifBadArg; | |
| } | |
| *alerts = mmio_region_read32(entropy_src->base_addr, | |
| ENTROPY_SRC_RECOV_ALERT_STS_REG_OFFSET); | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_clear_recoverable_alerts( | |
| const dif_entropy_src_t *entropy_src, uint32_t alerts) { | |
| if (entropy_src == NULL || alerts > kDifEntropySrcAlertAllAlerts) { | |
| return kDifBadArg; | |
| } | |
| uint32_t active_alerts = mmio_region_read32( | |
| entropy_src->base_addr, ENTROPY_SRC_RECOV_ALERT_STS_REG_OFFSET); | |
| active_alerts &= ~alerts; | |
| mmio_region_write32(entropy_src->base_addr, | |
| ENTROPY_SRC_RECOV_ALERT_STS_REG_OFFSET, active_alerts); | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_get_errors(const dif_entropy_src_t *entropy_src, | |
| uint32_t *errors) { | |
| if (entropy_src == NULL || errors == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t err_code_reg = mmio_region_read32(entropy_src->base_addr, | |
| ENTROPY_SRC_ERR_CODE_REG_OFFSET); | |
| *errors = 0; | |
| // ESRNG FIFO errors. | |
| if (bitfield_bit32_read(err_code_reg, | |
| ENTROPY_SRC_ERR_CODE_SFIFO_ESRNG_ERR_BIT)) { | |
| if (bitfield_bit32_read(err_code_reg, | |
| ENTROPY_SRC_ERR_CODE_FIFO_WRITE_ERR_BIT)) { | |
| *errors |= kDifEntropySrcErrorRngFifoWrite; | |
| } | |
| if (bitfield_bit32_read(err_code_reg, | |
| ENTROPY_SRC_ERR_CODE_FIFO_READ_ERR_BIT)) { | |
| *errors |= kDifEntropySrcErrorRngFifoRead; | |
| } | |
| if (bitfield_bit32_read(err_code_reg, | |
| ENTROPY_SRC_ERR_CODE_FIFO_STATE_ERR_BIT)) { | |
| *errors |= kDifEntropySrcErrorRngFifoState; | |
| } | |
| } | |
| // Observe FIFO errors. | |
| if (bitfield_bit32_read(err_code_reg, | |
| ENTROPY_SRC_ERR_CODE_SFIFO_OBSERVE_ERR_BIT)) { | |
| if (bitfield_bit32_read(err_code_reg, | |
| ENTROPY_SRC_ERR_CODE_FIFO_WRITE_ERR_BIT)) { | |
| *errors |= kDifEntropySrcErrorObserveFifoWrite; | |
| } | |
| if (bitfield_bit32_read(err_code_reg, | |
| ENTROPY_SRC_ERR_CODE_FIFO_READ_ERR_BIT)) { | |
| *errors |= kDifEntropySrcErrorObserveFifoRead; | |
| } | |
| if (bitfield_bit32_read(err_code_reg, | |
| ENTROPY_SRC_ERR_CODE_FIFO_STATE_ERR_BIT)) { | |
| *errors |= kDifEntropySrcErrorObserveFifoState; | |
| } | |
| } | |
| // ESFINAL FIFO errors. | |
| if (bitfield_bit32_read(err_code_reg, | |
| ENTROPY_SRC_ERR_CODE_SFIFO_ESFINAL_ERR_BIT)) { | |
| if (bitfield_bit32_read(err_code_reg, | |
| ENTROPY_SRC_ERR_CODE_FIFO_WRITE_ERR_BIT)) { | |
| *errors |= kDifEntropySrcErrorFinalFifoWrite; | |
| } | |
| if (bitfield_bit32_read(err_code_reg, | |
| ENTROPY_SRC_ERR_CODE_FIFO_READ_ERR_BIT)) { | |
| *errors |= kDifEntropySrcErrorFinalFifoRead; | |
| } | |
| if (bitfield_bit32_read(err_code_reg, | |
| ENTROPY_SRC_ERR_CODE_FIFO_STATE_ERR_BIT)) { | |
| *errors |= kDifEntropySrcErrorFinalFifoState; | |
| } | |
| } | |
| // Remaining FSM/Counter errors. | |
| if (bitfield_bit32_read(err_code_reg, | |
| ENTROPY_SRC_ERR_CODE_ES_ACK_SM_ERR_BIT)) { | |
| *errors |= kDifEntropySrcErrorAckStateMachine; | |
| } | |
| if (bitfield_bit32_read(err_code_reg, | |
| ENTROPY_SRC_ERR_CODE_ES_MAIN_SM_ERR_BIT)) { | |
| *errors |= kDifEntropySrcErrorMainStateMachine; | |
| } | |
| if (bitfield_bit32_read(err_code_reg, ENTROPY_SRC_ERR_CODE_ES_CNTR_ERR_BIT)) { | |
| *errors |= kDifEntropySrcErrorHardenedCounter; | |
| } | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_error_force(const dif_entropy_src_t *entropy_src, | |
| dif_entropy_src_error_t error) { | |
| if (entropy_src == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t err_code_reg = 0; | |
| switch (error) { | |
| case kDifEntropySrcErrorRngFifoWrite: | |
| case kDifEntropySrcErrorRngFifoRead: | |
| case kDifEntropySrcErrorRngFifoState: | |
| err_code_reg = ENTROPY_SRC_ERR_CODE_SFIFO_ESRNG_ERR_BIT; | |
| break; | |
| case kDifEntropySrcErrorObserveFifoWrite: | |
| case kDifEntropySrcErrorObserveFifoRead: | |
| case kDifEntropySrcErrorObserveFifoState: | |
| err_code_reg = ENTROPY_SRC_ERR_CODE_SFIFO_OBSERVE_ERR_BIT; | |
| break; | |
| case kDifEntropySrcErrorFinalFifoWrite: | |
| case kDifEntropySrcErrorFinalFifoRead: | |
| case kDifEntropySrcErrorFinalFifoState: | |
| err_code_reg = ENTROPY_SRC_ERR_CODE_SFIFO_ESFINAL_ERR_BIT; | |
| break; | |
| case kDifEntropySrcErrorAckStateMachine: | |
| err_code_reg = ENTROPY_SRC_ERR_CODE_ES_ACK_SM_ERR_BIT; | |
| break; | |
| case kDifEntropySrcErrorMainStateMachine: | |
| err_code_reg = ENTROPY_SRC_ERR_CODE_ES_MAIN_SM_ERR_BIT; | |
| break; | |
| case kDifEntropySrcErrorHardenedCounter: | |
| err_code_reg = ENTROPY_SRC_ERR_CODE_ES_CNTR_ERR_BIT; | |
| break; | |
| default: | |
| return kDifBadArg; | |
| } | |
| mmio_region_write32(entropy_src->base_addr, | |
| ENTROPY_SRC_ERR_CODE_TEST_REG_OFFSET, err_code_reg); | |
| return kDifOk; | |
| } | |
| dif_result_t dif_entropy_src_get_main_fsm_state( | |
| const dif_entropy_src_t *entropy_src, dif_entropy_src_main_fsm_t *state) { | |
| if (entropy_src == NULL || state == NULL) { | |
| return kDifBadArg; | |
| } | |
| *state = mmio_region_read32(entropy_src->base_addr, | |
| ENTROPY_SRC_MAIN_SM_STATE_REG_OFFSET); | |
| return kDifOk; | |
| } |