Skip to content

Commit

Permalink
[entropy_src,sival] Implement entropy_src_fw_observe_many_contiguous
Browse files Browse the repository at this point in the history
Signed-off-by: Amaury Pouly <[email protected]>
(cherry picked from commit 60a31ea)
  • Loading branch information
pamaury committed Feb 15, 2024
1 parent 3d405b5 commit 4a7c5b0
Show file tree
Hide file tree
Showing 3 changed files with 253 additions and 1 deletion.
2 changes: 1 addition & 1 deletion hw/top_earlgrey/data/ip/chip_entropy_src_testplan.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@
si_stage: SV3
lc_states: ["TEST_UNLOCKED", "PROD"]
tests: []
bazel: []
bazel: ["//sw/device/tests:entropy_src_fw_observe_many_contiguous_test"]
},
{
name: chip_sw_entropy_src_fw_extract_and_insert
Expand Down
24 changes: 24 additions & 0 deletions sw/device/tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -1302,6 +1302,30 @@ opentitan_test(
],
)

opentitan_test(
name = "entropy_src_fw_observe_many_contiguous_test",
srcs = ["entropy_src_fw_observe_many_contiguous.c"],
exec_env = dicts.add(
EARLGREY_TEST_ENVS,
EARLGREY_SILICON_OWNER_ROM_EXT_ENVS,
{
"//hw/top_earlgrey:silicon_creator": None,
},
),
deps = [
"//hw/ip/entropy_src/data:entropy_src_regs",
"//hw/top_earlgrey/sw/autogen:top_earlgrey",
"//sw/device/lib/base:memory",
"//sw/device/lib/base:mmio",
"//sw/device/lib/dif:base",
"//sw/device/lib/dif:entropy_src",
"//sw/device/lib/runtime:log",
"//sw/device/lib/testing:edn_testutils",
"//sw/device/lib/testing:entropy_testutils",
"//sw/device/lib/testing/test_framework:ottf_main",
],
)

opentitan_test(
name = "entropy_src_edn_reqs_test",
srcs = ["entropy_src_edn_reqs_test.c"],
Expand Down
228 changes: 228 additions & 0 deletions sw/device/tests/entropy_src_fw_observe_many_contiguous.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
// 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/base/memory.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/dif/dif_base.h"
#include "sw/device/lib/dif/dif_entropy_src.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/edn_testutils.h"
#include "sw/device/lib/testing/entropy_testutils.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_main.h"

#include "entropy_src_regs.h" // Generated.
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" // Generated.

OTTF_DEFINE_TEST_CONFIG();

enum {
kEntropySrcHealthTestWindowSize = 0x200,
/**
* Observe FIFO threshold: half of the FIFO size.
*/
kEntropySrcFifoThreshold = 32,
/**
* The number of contiguous samples we want to capture.
*/
kContiguousSamplesCount = 1024,
/*
* Timeout to read kContiguousSamplesCount.
*/
kTimeoutUsec = 1000 * 1000,
/**
* Number of time to repeat each test, letting the observe FIFO
* overflow in-between tests.
*/
kRepeatCount = 4,
/**
* Number of bits per sample.
*/
kBitsPerSample = 4,
/**
* Size of buffer in words to hold all the samples, assuming
* 4-bit samples at the most.
*/
kFifoBufferSizeWords =
kContiguousSamplesCount * kBitsPerSample / sizeof(uint32_t),
};

static uint32_t sample_buffer[kFifoBufferSizeWords];
static dif_entropy_src_t entropy_src;
static dif_csrng_t csrng;
static dif_edn_t edn0;
static dif_edn_t edn1;

/**
* Determine whether the observe FIFO has overflowed.
*
* TODO(#21279) Normally, one would rely on the FW_OV_RD_FIFO_OVERFLOW
* register but due to an RTL bug, the overflow bit is pulsed
* instead of latched so we cannot rely on it. Instead, rely
* on OBSERVE_FIFO_DEPTH and assume that if the FIFO is full
* then it has overflowed.
*/
bool entropy_src_fifo_has_overflowed(void) {
uint32_t fifo_depth;
CHECK_DIF_OK(dif_entropy_src_get_fifo_depth(&entropy_src, &fifo_depth));
return fifo_depth == ENTROPY_SRC_PARAM_OBSERVE_FIFO_DEPTH;
}

/**
* Drain observe FIFO and clear overflow status if set.
*/
static void drain_observe_fifo(void) {
// This value is arbitrary, it could be 1 but since there is some
// overhead in dif_entropy_src_observe_fifo_nonblocking_read, it's better
// to read several words every time to drain the FIFO quickly.
const size_t kDrainCount = 32;
size_t len;
// Read from the FIFO until we get a short read which means that the FIFO was
// emptied.
LOG_INFO("drain observe FIFO overflow...");
do {
len = kDrainCount;
CHECK_DIF_OK(dif_entropy_src_observe_fifo_nonblocking_read(&entropy_src,
NULL, &len));
} while (len == kDrainCount);
CHECK_DIF_OK(dif_entropy_src_clear_fifo_overflow(&entropy_src));
}

/**
* Let observe FIFO overflow.
*/
static status_t let_observe_fifo_overflow(uint32_t timeout_usec) {
LOG_INFO("let observe FIFO overflow...");
IBEX_TRY_SPIN_FOR(entropy_src_fifo_has_overflowed(), timeout_usec);
return OK_STATUS();
}

// Configure the entropy complex.
static status_t entropy_config(
dif_entropy_src_single_bit_mode_t single_bit_mode) {
dif_edn_auto_params_t edn_params0 =
edn_testutils_auto_params_build(false, /*res_itval=*/0, /*glen_val=*/0);
dif_edn_auto_params_t edn_params1 =
edn_testutils_auto_params_build(false, /*res_itval=*/0, /*glen_val=*/0);
// Disable the entropy complex.
TRY(entropy_testutils_stop_all());

// Disable all health tests.
static dif_entropy_src_test_t kHealthTest[] = {
kDifEntropySrcTestRepetitionCount,
kDifEntropySrcTestRepetitionCountSymbol,
kDifEntropySrcTestAdaptiveProportion, kDifEntropySrcTestBucket,
kDifEntropySrcTestMarkov};
for (size_t i = 0; i < ARRAYSIZE(kHealthTest); i++) {
TRY(dif_entropy_src_health_test_configure(
&entropy_src,
(dif_entropy_src_health_test_config_t){.test_type = kHealthTest[i],
.high_threshold = 0xffffffff,
.low_threshold = 0}));
}
// Enable FW override.
TRY(dif_entropy_src_fw_override_configure(
&entropy_src,
(dif_entropy_src_fw_override_config_t){
.entropy_insert_enable = false,
.buffer_threshold = kEntropySrcFifoThreshold,
},
kDifToggleEnabled));
// Enable entropy_src.
TRY(dif_entropy_src_configure(
&entropy_src,
(dif_entropy_src_config_t){
.fips_enable = true,
.route_to_firmware = false,
.bypass_conditioner = false,
.single_bit_mode = single_bit_mode,
.health_test_threshold_scope = false,
.health_test_window_size = kEntropySrcHealthTestWindowSize,
.alert_threshold = UINT16_MAX},
kDifToggleEnabled));

// Enable CSRNG
TRY(dif_csrng_configure(&csrng));
// Enable EDNs in auto request mode
TRY(dif_edn_set_auto_mode(&edn0, edn_params0));
TRY(dif_edn_set_auto_mode(&edn1, edn_params1));
return OK_STATUS();
}

/**
* Test the firmware override observe path.
*
* @param entropy An Entropy handle.
*/
status_t firmware_override_observe(
uint32_t nr_samples, dif_entropy_src_single_bit_mode_t single_bit_mode,
uint32_t timeout_usec, uint32_t repeat_count) {
// Slow computation: do it once.
uint32_t nr_sample_words =
ceil_div(nr_samples * kBitsPerSample, sizeof(uint32_t));
// Configure the entropy complex.
entropy_config(single_bit_mode);

LOG_INFO("==================");
LOG_INFO("Running test in mode %u, will run test %u times", single_bit_mode,
repeat_count);
while (repeat_count-- > 0) {
LOG_INFO("collecting %u samples...", nr_samples);
// Collect samples from the the observe FIFO.
uint32_t words_to_read = nr_sample_words;
uint32_t *sample_buffer_ptr = sample_buffer;
// Drain FIFO to make sure we get contiguous samples.
drain_observe_fifo();
// Collect.
ibex_timeout_t tmo = ibex_timeout_init(timeout_usec);
while (words_to_read > 0 && !ibex_timeout_check(&tmo)) {
size_t len = words_to_read;
// Check FIFO did not overflow during collection.
TRY_CHECK(!entropy_src_fifo_has_overflowed(),
"observe FIFO overflowed during collection");
TRY(dif_entropy_src_observe_fifo_nonblocking_read(
&entropy_src, sample_buffer_ptr, &len));
sample_buffer_ptr += len;
words_to_read -= len;
}
TRY_CHECK(!ibex_timeout_check(&tmo), "did not collect samples in time");
// Make sure the FIFO did not overflow.
uint64_t elapsed = ibex_timeout_elapsed(&tmo);
uint64_t freq =
udiv64_slow((uint64_t)nr_samples * (uint64_t)1000000, elapsed, NULL);
LOG_INFO("done in %ums (~ %usamples/s)",
(uint32_t)udiv64_slow(elapsed, 1000, NULL), (uint32_t)freq);

// Let observe FIFO overflow
if (repeat_count > 0) {
TRY(let_observe_fifo_overflow(timeout_usec));
}
}
return OK_STATUS();
}

bool test_main(void) {
CHECK_DIF_OK(dif_entropy_src_init(
mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR), &entropy_src));
CHECK_DIF_OK(dif_csrng_init(
mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR), &csrng));
CHECK_DIF_OK(
dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN0_BASE_ADDR), &edn0));
CHECK_DIF_OK(
dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR), &edn1));
// Test all modes.
static dif_entropy_src_single_bit_mode_t kModes[] = {
kDifEntropySrcSingleBitModeDisabled, kDifEntropySrcSingleBitMode0,
kDifEntropySrcSingleBitMode1, kDifEntropySrcSingleBitMode2,
kDifEntropySrcSingleBitMode3,
};
status_t test_result = OK_STATUS();
for (size_t i = 0; i < ARRAYSIZE(kModes); i++) {
EXECUTE_TEST(test_result, firmware_override_observe,
kContiguousSamplesCount, kModes[i], kTimeoutUsec,
kRepeatCount);
}

return status_ok(test_result);
}

0 comments on commit 4a7c5b0

Please sign in to comment.