|
| 1 | +// Copyright lowRISC contributors. |
| 2 | +// Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 3 | +// SPDX-License-Identifier: Apache-2.0 |
| 4 | + |
| 5 | +#include "sw/device/lib/base/memory.h" |
| 6 | +#include "sw/device/lib/base/mmio.h" |
| 7 | +#include "sw/device/lib/dif/dif_base.h" |
| 8 | +#include "sw/device/lib/dif/dif_entropy_src.h" |
| 9 | +#include "sw/device/lib/runtime/log.h" |
| 10 | +#include "sw/device/lib/testing/edn_testutils.h" |
| 11 | +#include "sw/device/lib/testing/entropy_testutils.h" |
| 12 | +#include "sw/device/lib/testing/test_framework/check.h" |
| 13 | +#include "sw/device/lib/testing/test_framework/ottf_main.h" |
| 14 | + |
| 15 | +#include "entropy_src_regs.h" // Generated. |
| 16 | +#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" // Generated. |
| 17 | + |
| 18 | +OTTF_DEFINE_TEST_CONFIG(); |
| 19 | + |
| 20 | +enum { |
| 21 | + kEntropySrcHealthTestWindowSize = 0x200, |
| 22 | + /** |
| 23 | + * Observe FIFO threshold: half of the FIFO size. |
| 24 | + */ |
| 25 | + kEntropySrcFifoThreshold = 32, |
| 26 | + /** |
| 27 | + * The number of contiguous samples we want to capture. |
| 28 | + */ |
| 29 | + kContiguousSamplesCount = 1024, |
| 30 | + /* |
| 31 | + * Timeout to read kContiguousSamplesCount. |
| 32 | + */ |
| 33 | + kTimeoutUsec = 1000 * 1000, |
| 34 | + /** |
| 35 | + * Number of time to repeat each test, letting the observe FIFO |
| 36 | + * overflow in-between tests. |
| 37 | + */ |
| 38 | + kRepeatCount = 4, |
| 39 | + /** |
| 40 | + * Number of bits per sample. |
| 41 | + */ |
| 42 | + kBitsPerSample = 4, |
| 43 | + /** |
| 44 | + * Size of buffer in words to hold all the samples, assuming |
| 45 | + * 4-bit samples at the most. |
| 46 | + */ |
| 47 | + kFifoBufferSizeWords = |
| 48 | + kContiguousSamplesCount * kBitsPerSample / sizeof(uint32_t), |
| 49 | +}; |
| 50 | + |
| 51 | +static uint32_t sample_buffer[kFifoBufferSizeWords]; |
| 52 | +static dif_entropy_src_t entropy_src; |
| 53 | +static dif_csrng_t csrng; |
| 54 | +static dif_edn_t edn0; |
| 55 | +static dif_edn_t edn1; |
| 56 | + |
| 57 | +/** |
| 58 | + * Determine whether the observe FIFO has overflowed. |
| 59 | + * |
| 60 | + * TODO(#21279) Normally, one would rely on the FW_OV_RD_FIFO_OVERFLOW |
| 61 | + * register but due to an RTL bug, the overflow bit is pulsed |
| 62 | + * instead of latched so we cannot rely on it. Instead, rely |
| 63 | + * on OBSERVE_FIFO_DEPTH and assume that if the FIFO is full |
| 64 | + * then it has overflowed. |
| 65 | + */ |
| 66 | +bool entropy_src_fifo_has_overflowed(void) { |
| 67 | + uint32_t fifo_depth; |
| 68 | + CHECK_DIF_OK(dif_entropy_src_get_fifo_depth(&entropy_src, &fifo_depth)); |
| 69 | + return fifo_depth == ENTROPY_SRC_PARAM_OBSERVE_FIFO_DEPTH; |
| 70 | +} |
| 71 | + |
| 72 | +/** |
| 73 | + * Drain observe FIFO and clear overflow status if set. |
| 74 | + */ |
| 75 | +static void drain_observe_fifo(void) { |
| 76 | + // This value is arbitrary, it could be 1 but since there is some |
| 77 | + // overhead in dif_entropy_src_observe_fifo_nonblocking_read, it's better |
| 78 | + // to read several words every time to drain the FIFO quickly. |
| 79 | + const size_t kDrainCount = 32; |
| 80 | + size_t len; |
| 81 | + // Read from the FIFO until we get a short read which means that the FIFO was |
| 82 | + // emptied. |
| 83 | + LOG_INFO("drain observe FIFO overflow..."); |
| 84 | + do { |
| 85 | + len = kDrainCount; |
| 86 | + CHECK_DIF_OK(dif_entropy_src_observe_fifo_nonblocking_read(&entropy_src, |
| 87 | + NULL, &len)); |
| 88 | + } while (len == kDrainCount); |
| 89 | + CHECK_DIF_OK(dif_entropy_src_clear_fifo_overflow(&entropy_src)); |
| 90 | +} |
| 91 | + |
| 92 | +/** |
| 93 | + * Let observe FIFO overflow. |
| 94 | + */ |
| 95 | +static status_t let_observe_fifo_overflow(uint32_t timeout_usec) { |
| 96 | + LOG_INFO("let observe FIFO overflow..."); |
| 97 | + IBEX_TRY_SPIN_FOR(entropy_src_fifo_has_overflowed(), timeout_usec); |
| 98 | + return OK_STATUS(); |
| 99 | +} |
| 100 | + |
| 101 | +// Configure the entropy complex. |
| 102 | +static status_t entropy_config( |
| 103 | + dif_entropy_src_single_bit_mode_t single_bit_mode) { |
| 104 | + dif_edn_auto_params_t edn_params0 = |
| 105 | + edn_testutils_auto_params_build(false, /*res_itval=*/0, /*glen_val=*/0); |
| 106 | + dif_edn_auto_params_t edn_params1 = |
| 107 | + edn_testutils_auto_params_build(false, /*res_itval=*/0, /*glen_val=*/0); |
| 108 | + // Disable the entropy complex. |
| 109 | + TRY(entropy_testutils_stop_all()); |
| 110 | + |
| 111 | + // Disable all health tests. |
| 112 | + static dif_entropy_src_test_t kHealthTest[] = { |
| 113 | + kDifEntropySrcTestRepetitionCount, |
| 114 | + kDifEntropySrcTestRepetitionCountSymbol, |
| 115 | + kDifEntropySrcTestAdaptiveProportion, kDifEntropySrcTestBucket, |
| 116 | + kDifEntropySrcTestMarkov}; |
| 117 | + for (size_t i = 0; i < ARRAYSIZE(kHealthTest); i++) { |
| 118 | + TRY(dif_entropy_src_health_test_configure( |
| 119 | + &entropy_src, |
| 120 | + (dif_entropy_src_health_test_config_t){.test_type = kHealthTest[i], |
| 121 | + .high_threshold = 0xffffffff, |
| 122 | + .low_threshold = 0})); |
| 123 | + } |
| 124 | + // Enable FW override. |
| 125 | + TRY(dif_entropy_src_fw_override_configure( |
| 126 | + &entropy_src, |
| 127 | + (dif_entropy_src_fw_override_config_t){ |
| 128 | + .entropy_insert_enable = false, |
| 129 | + .buffer_threshold = kEntropySrcFifoThreshold, |
| 130 | + }, |
| 131 | + kDifToggleEnabled)); |
| 132 | + // Enable entropy_src. |
| 133 | + TRY(dif_entropy_src_configure( |
| 134 | + &entropy_src, |
| 135 | + (dif_entropy_src_config_t){ |
| 136 | + .fips_enable = true, |
| 137 | + .route_to_firmware = false, |
| 138 | + .bypass_conditioner = false, |
| 139 | + .single_bit_mode = single_bit_mode, |
| 140 | + .health_test_threshold_scope = false, |
| 141 | + .health_test_window_size = kEntropySrcHealthTestWindowSize, |
| 142 | + .alert_threshold = UINT16_MAX}, |
| 143 | + kDifToggleEnabled)); |
| 144 | + |
| 145 | + // Enable CSRNG |
| 146 | + TRY(dif_csrng_configure(&csrng)); |
| 147 | + // Enable EDNs in auto request mode |
| 148 | + TRY(dif_edn_set_auto_mode(&edn0, edn_params0)); |
| 149 | + TRY(dif_edn_set_auto_mode(&edn1, edn_params1)); |
| 150 | + return OK_STATUS(); |
| 151 | +} |
| 152 | + |
| 153 | +/** |
| 154 | + * Test the firmware override observe path. |
| 155 | + * |
| 156 | + * @param entropy An Entropy handle. |
| 157 | + */ |
| 158 | +status_t firmware_override_observe( |
| 159 | + uint32_t nr_samples, dif_entropy_src_single_bit_mode_t single_bit_mode, |
| 160 | + uint32_t timeout_usec, uint32_t repeat_count) { |
| 161 | + // Slow computation: do it once. |
| 162 | + uint32_t nr_sample_words = |
| 163 | + ceil_div(nr_samples * kBitsPerSample, sizeof(uint32_t)); |
| 164 | + // Configure the entropy complex. |
| 165 | + entropy_config(single_bit_mode); |
| 166 | + |
| 167 | + LOG_INFO("=================="); |
| 168 | + LOG_INFO("Running test in mode %u, will run test %u times", single_bit_mode, |
| 169 | + repeat_count); |
| 170 | + while (repeat_count-- > 0) { |
| 171 | + LOG_INFO("collecting %u samples...", nr_samples); |
| 172 | + // Collect samples from the the observe FIFO. |
| 173 | + uint32_t words_to_read = nr_sample_words; |
| 174 | + uint32_t *sample_buffer_ptr = sample_buffer; |
| 175 | + // Drain FIFO to make sure we get contiguous samples. |
| 176 | + drain_observe_fifo(); |
| 177 | + // Collect. |
| 178 | + ibex_timeout_t tmo = ibex_timeout_init(timeout_usec); |
| 179 | + while (words_to_read > 0 && !ibex_timeout_check(&tmo)) { |
| 180 | + size_t len = words_to_read; |
| 181 | + // Check FIFO did not overflow during collection. |
| 182 | + TRY_CHECK(!entropy_src_fifo_has_overflowed(), |
| 183 | + "observe FIFO overflowed during collection"); |
| 184 | + TRY(dif_entropy_src_observe_fifo_nonblocking_read( |
| 185 | + &entropy_src, sample_buffer_ptr, &len)); |
| 186 | + sample_buffer_ptr += len; |
| 187 | + words_to_read -= len; |
| 188 | + } |
| 189 | + TRY_CHECK(!ibex_timeout_check(&tmo), "did not collect samples in time"); |
| 190 | + // Make sure the FIFO did not overflow. |
| 191 | + uint64_t elapsed = ibex_timeout_elapsed(&tmo); |
| 192 | + uint64_t freq = |
| 193 | + udiv64_slow((uint64_t)nr_samples * (uint64_t)1000000, elapsed, NULL); |
| 194 | + LOG_INFO("done in %ums (~ %usamples/s)", |
| 195 | + (uint32_t)udiv64_slow(elapsed, 1000, NULL), (uint32_t)freq); |
| 196 | + |
| 197 | + // Let observe FIFO overflow |
| 198 | + if (repeat_count > 0) { |
| 199 | + TRY(let_observe_fifo_overflow(timeout_usec)); |
| 200 | + } |
| 201 | + } |
| 202 | + return OK_STATUS(); |
| 203 | +} |
| 204 | + |
| 205 | +bool test_main(void) { |
| 206 | + CHECK_DIF_OK(dif_entropy_src_init( |
| 207 | + mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR), &entropy_src)); |
| 208 | + CHECK_DIF_OK(dif_csrng_init( |
| 209 | + mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR), &csrng)); |
| 210 | + CHECK_DIF_OK( |
| 211 | + dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN0_BASE_ADDR), &edn0)); |
| 212 | + CHECK_DIF_OK( |
| 213 | + dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR), &edn1)); |
| 214 | + // Test all modes. |
| 215 | + static dif_entropy_src_single_bit_mode_t kModes[] = { |
| 216 | + kDifEntropySrcSingleBitModeDisabled, kDifEntropySrcSingleBitMode0, |
| 217 | + kDifEntropySrcSingleBitMode1, kDifEntropySrcSingleBitMode2, |
| 218 | + kDifEntropySrcSingleBitMode3, |
| 219 | + }; |
| 220 | + status_t test_result = OK_STATUS(); |
| 221 | + for (size_t i = 0; i < ARRAYSIZE(kModes); i++) { |
| 222 | + EXECUTE_TEST(test_result, firmware_override_observe, |
| 223 | + kContiguousSamplesCount, kModes[i], kTimeoutUsec, |
| 224 | + kRepeatCount); |
| 225 | + } |
| 226 | + |
| 227 | + return status_ok(test_result); |
| 228 | +} |
0 commit comments