blob: 2667ac4eec294d016a51078e6b625e327bcd8ec6 [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/lib/base/macros.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/dif/dif_csrng.h"
#include "sw/device/lib/dif/dif_csrng_shared.h"
#include "sw/device/lib/dif/dif_edn.h"
#include "sw/device/lib/dif/dif_entropy_src.h"
#include "sw/device/lib/runtime/irq.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/csrng_testutils.h"
#include "sw/device/lib/testing/entropy_testutils.h"
#include "sw/device/lib/testing/otbn_testutils.h"
#include "sw/device/lib/testing/rand_testutils.h"
#include "sw/device/lib/testing/rv_plic_testutils.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_macros.h"
#include "sw/device/lib/testing/test_framework/ottf_main.h"
#include "sw/device/tests/otbn_randomness_impl.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
#include "sw/device/lib/testing/autogen/isr_testutils.h"
static dif_csrng_t csrng;
static dif_edn_t edn0;
static dif_edn_t edn1;
static dif_entropy_src_t entropy_src;
static dif_otbn_t otbn;
static dif_rv_plic_t plic;
static dif_rv_core_ibex_t rv_core_ibex;
enum {
/**
* The number of test iterations per target.
*/
kTestParamNumIterationsSim = 2,
kTestParamNumIterationsOther = 20,
/**
* The number of test iterations per entropy consumer.
*/
kTestParamNumOtbnIterationsMax = 4,
kTestParamNumIbexIterationsMax = 16,
};
/**
* Test execution states. These are use to control the execution state of all
* test tasks.
*/
typedef enum test_state {
/**
* Configure entropy complex for next test iteration.
*/
kTestStateSetup,
/**
* Request entropy from various endpoints.
*/
kTestStateRun,
/**
* Tear down tasks.
*/
kTestStateTearDown,
/**
* Number of test states.
*/
kTestStateCount,
} test_state_t;
static volatile test_state_t execution_state;
static const char *kStatusNames[kTestStateCount] = {"Setup", "Run", "TearDown"};
static_assert(ARRAYSIZE(kStatusNames) == kTestStateCount,
"Unexpected kStatusNames array size.");
/**
* Each task uses the task ID to update flag and counter arrays.
*/
typedef enum task_id {
/**
* Assigned to `main_task()`.
*/
kTestTaskIdMain,
/**
* Assigned to `otbn_task()`.
*/
kTestTaskIdOtbn,
/**
* Assigned to `ibex_task()`.
*/
kTestTaskIdIbex,
/**
* Number of tasks. Used to define task flag and counter arrays.
*/
kTestTaskIdCount
} task_id_t;
/**
* Flags used to track when a task is done executing a test iteration.
*/
static volatile bool task_done[kTestTaskIdCount];
/**
* Flags used to track the number of test iterations per task.
*/
static volatile uint32_t task_iter_count_max[kTestTaskIdCount];
OTTF_DEFINE_TEST_CONFIG(.enable_concurrency = true,
.can_clobber_uart = false, );
/**
* Initializes the peripherals used in this test.
*/
static void init_peripherals(void) {
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));
CHECK_DIF_OK(dif_entropy_src_init(
mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR), &entropy_src));
CHECK_DIF_OK(dif_rv_plic_init(
mmio_region_from_addr(TOP_EARLGREY_RV_PLIC_BASE_ADDR), &plic));
CHECK_DIF_OK(dif_rv_core_ibex_init(
mmio_region_from_addr(TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR),
&rv_core_ibex));
CHECK_DIF_OK(
dif_otbn_init(mmio_region_from_addr(TOP_EARLGREY_OTBN_BASE_ADDR), &otbn));
}
/**
* Returns randomized seed material.
*/
static dif_edn_seed_material_t edn_seed_material_build(void) {
dif_edn_seed_material_t seed;
seed.len =
rand_testutils_gen32_range(/*min=*/0, kDifEntropySeedMaterialMaxWordLen);
for (size_t i = 0; i < seed.len; ++i) {
seed.data[i] = rand_testutils_gen32();
}
return seed;
}
/**
* Returns a randomized EDN auto mode configuration.
*/
static dif_edn_auto_params_t edn_auto_params_build(void) {
dif_edn_seed_material_t seed0 = edn_seed_material_build();
dif_edn_seed_material_t seed1 = edn_seed_material_build();
return (dif_edn_auto_params_t){
.instantiate_cmd =
{
.cmd = csrng_cmd_header_build(kCsrngAppCmdInstantiate,
kDifCsrngEntropySrcToggleEnable,
seed0.len,
/*generate_len=*/0),
.seed_material = seed0,
},
.reseed_cmd =
{
.cmd = csrng_cmd_header_build(kCsrngAppCmdReseed,
kDifCsrngEntropySrcToggleEnable,
seed1.len,
/*generate_len=*/0),
.seed_material = seed1,
},
.generate_cmd =
{
.cmd = csrng_cmd_header_build(kCsrngAppCmdGenerate,
kDifCsrngEntropySrcToggleEnable,
/*cmd_len=*/0,
/*generate_len=*/1),
.seed_material =
{
.len = 0,
},
},
.reseed_interval = 4,
};
}
/**
* Helper function used to set the task done flag.
*
* Issues a `ottf_task_yield()` to ensure control is handed over to pending
* tasks.
*
* @param task_id The task ID.
*/
static void task_done_set_and_yield(task_id_t task_id) {
task_done[task_id] = true;
ottf_task_yield();
}
/**
* OTBN task.
*
* Executes OTBN randomness test the the test state is set to `kTestStateRun`.
*
* @param task_parameters Unused. Set to NULL by ottf.
*/
static void otbn_task(void *task_parameters) {
while (true) {
if (execution_state == kTestStateTearDown) {
break;
}
if (execution_state == kTestStateSetup || task_done[kTestTaskIdOtbn]) {
ottf_task_yield();
continue;
}
LOG_INFO("OTBN:START");
for (size_t i = 0; i < task_iter_count_max[kTestTaskIdOtbn]; ++i) {
otbn_randomness_test_start(&otbn);
dif_otbn_status_t status;
do {
CHECK_DIF_OK(dif_otbn_get_status(&otbn, &status));
ottf_task_yield();
} while (status != kDifOtbnStatusIdle && status != kDifOtbnStatusLocked);
CHECK(otbn_randomness_test_end(&otbn, /*skip_otbn_done_check=*/false));
}
LOG_INFO("OTBN:DONE");
task_done_set_and_yield(kTestTaskIdOtbn);
}
OTTF_TASK_DELETE_SELF_OR_DIE;
}
/**
* Ibex task.
*
* Executes Ibex randomness test the the test state is set to `kTestStateRun`.
*
* @param task_parameters Unused. Set to NULL by ottf.
*/
static void ibex_task(void *task_parameters) {
while (true) {
if (execution_state == kTestStateTearDown) {
break;
}
if (execution_state == kTestStateSetup || task_done[kTestTaskIdIbex]) {
ottf_task_yield();
continue;
}
LOG_INFO("Ibex:START");
uint32_t prev_data = 0;
for (size_t i = 0; i < task_iter_count_max[kTestTaskIdIbex];) {
dif_rv_core_ibex_rnd_status_t status;
CHECK_DIF_OK(dif_rv_core_ibex_get_rnd_status(&rv_core_ibex, &status));
if ((status & kDifRvCoreIbexRndStatusValid) == 0) {
ottf_task_yield();
continue;
}
i++;
uint32_t new_data;
CHECK_DIF_OK(dif_rv_core_ibex_read_rnd_data(&rv_core_ibex, &new_data));
// The following checks have a probability of returning false negatives.
// Flakiness issues can potentially be addressed by switching to error
// counters.
CHECK(new_data != prev_data, "Unexpected duplicate data");
CHECK(new_data != 0);
CHECK(new_data != UINT32_MAX);
prev_data = new_data;
}
LOG_INFO("Ibex:DONE");
task_done_set_and_yield(kTestTaskIdIbex);
}
OTTF_TASK_DELETE_SELF_OR_DIE;
}
/**
* Verifies that the entropy req interrupt is triggered on EDN instantiate and
* reseed commands.
*/
static void entropy_config(void) {
entropy_testutils_auto_mode_init();
LOG_INFO("Generating EDN params");
dif_edn_auto_params_t edn_params0 = edn_auto_params_build();
dif_edn_auto_params_t edn_params1 = edn_auto_params_build();
task_iter_count_max[kTestTaskIdOtbn] =
rand_testutils_gen32_range(/*min=*/1, kTestParamNumOtbnIterationsMax);
task_iter_count_max[kTestTaskIdIbex] =
rand_testutils_gen32_range(/*min=*/1, kTestParamNumIbexIterationsMax);
entropy_testutils_stop_all();
CHECK_DIF_OK(dif_entropy_src_configure(
&entropy_src, entropy_testutils_config_default(), kDifToggleEnabled));
CHECK_DIF_OK(dif_csrng_configure(&csrng));
CHECK_DIF_OK(dif_edn_set_auto_mode(&edn0, edn_params0));
CHECK_DIF_OK(dif_edn_set_auto_mode(&edn1, edn_params1));
}
/**
* Updates and logs the execution state.
*
* @param next_state The next state.
*/
static void execution_state_update(test_state_t next_state) {
CHECK(next_state < kTestStateCount);
LOG_INFO("Test state: %s", kStatusNames[next_state]);
execution_state = next_state;
}
/**
* Main test task.
*
* Configures entropy complex and signals test tasks to run an iteration loop.
*
* @param task_parameters Unused. Set to NULL by ottf.
*/
static void main_task(void *task_parameters) {
task_iter_count_max[kTestTaskIdMain] = kTestParamNumIterationsSim;
if (kDeviceType != kDeviceSimDV && kDeviceType != kDeviceSimVerilator) {
task_iter_count_max[kTestTaskIdMain] = kTestParamNumIterationsOther;
}
uint32_t iter_count = 0;
while (true) {
if (execution_state == kTestStateRun) {
uint32_t count = 0;
for (size_t i = 0; i < kTestTaskIdCount; ++i) {
count += task_done[i] ? 1 : 0;
}
if (count < kTestTaskIdCount) {
ottf_task_yield();
continue;
}
// Consider this test iteration done if all tasks are done. Update the
// iteration counter and set the execution state to `kTestStateSetup`
// to continue with configuring the entropy blocks for the next test
// run iteration.
if (++iter_count >= task_iter_count_max[kTestTaskIdMain]) {
execution_state_update(kTestStateTearDown);
break;
}
csrng_testutils_recoverable_alerts_check(&csrng);
entropy_testutils_error_check(&entropy_src, &csrng, &edn0, &edn1);
execution_state_update(kTestStateSetup);
}
// The rest of this code block is executed when
// `execute_state == kTestStateSetup`.
entropy_config();
for (size_t i = 0; i < kTestTaskIdCount; ++i) {
task_done[i] = false;
}
// Signal the other tasks to start test execution.
execution_state_update(kTestStateRun);
// This is required to ensure the aggregation of task_done entries is equal
// to `kTestTaskIdCount`.
task_done_set_and_yield(kTestTaskIdMain);
}
OTTF_TASK_DELETE_SELF_OR_DIE;
}
bool test_main(void) {
init_peripherals();
// Initialize the test `execution_state` before launching the tasks.
execution_state_update(kTestStateSetup);
CHECK(ottf_task_create(main_task, "main", kOttfFreeRtosMinStackSize, 1));
CHECK(ottf_task_create(otbn_task, "otbn", kOttfFreeRtosMinStackSize, 1));
CHECK(ottf_task_create(ibex_task, "ibex", kOttfFreeRtosMinStackSize, 1));
// There is no need to poll for completion as the tasks above will execute
// with higher priority and control will not be returned to this task until
// all tasks are done executing.
ottf_task_yield();
return true;
}