| // 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; |
| } |