blob: b1cd3c65a0f91c5806bc871e3a92f9b956348fb5 [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_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/rv_plic_testutils.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_main.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_rv_plic_t plic;
// Set by `ottf_external_isr()` and cleared by
// `csrng_entropy_req_irq_enable()`.
static volatile bool entropy_src_isr_csrng_req_set;
OTTF_DEFINE_TEST_CONFIG();
enum {
/**
* The size of the buffer used in firmware to process the entropy bits in
* firmware override mode.
*/
kTestParamFifoBufferSize = 12,
/**
* The number of test iterations per target.
*/
kTestParamNumIterationsSim = 1,
kTestParamNumIterationsOther = 100,
};
/**
* 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));
}
/**
* Enables the CSRNG entropy request interrupt.
*/
static void csrng_entropy_req_irq_enable(void) {
irq_external_ctrl(false);
irq_global_ctrl(false);
entropy_src_isr_csrng_req_set = false;
dif_rv_plic_irq_id_t irq_id = kTopEarlgreyPlicIrqIdCsrngCsEntropyReq;
CHECK_DIF_OK(dif_rv_plic_irq_set_priority(&plic, irq_id, /*priority=*/1u));
CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(
&plic, irq_id, kTopEarlgreyPlicTargetIbex0, kDifToggleEnabled));
CHECK_DIF_OK(dif_rv_plic_target_set_threshold(
&plic, kTopEarlgreyPlicTargetIbex0, /*threshold=*/0u));
CHECK_DIF_OK(dif_csrng_irq_set_enabled(&csrng, kDifCsrngIrqCsEntropyReq,
kDifToggleEnabled));
irq_global_ctrl(true);
irq_external_ctrl(true);
}
/**
* Blocks until a CSRNG entropy request interrupt is received.
*/
static void csrng_entropy_req_irq_block_wait(void) {
// The interrupt can come before we enter sleep, so we check beforehand.
while (true) {
if (entropy_src_isr_csrng_req_set) {
break;
}
wait_for_interrupt();
}
CHECK_DIF_OK(dif_csrng_irq_set_enabled(&csrng, kDifCsrngIrqCsEntropyReq,
kDifToggleDisabled));
}
/**
* Requests data from CSRNG software instance.
*
* Asserts error if there are any repeated words in the output data, or if there
* are any errors set in the CSRNG status registers.
*/
static void csrng_generate_output_check(void) {
uint32_t output[kTestParamFifoBufferSize] = {0};
csrng_testutils_cmd_generate_run(&csrng, output, ARRAYSIZE(output));
uint32_t prev_data = 0;
for (size_t i = 0; i < ARRAYSIZE(output); ++i) {
CHECK(prev_data != output[i],
"Received duplicate data. Last index: %d, value: 0x%x", i, prev_data);
}
}
/**
* Verifies that the entropy req interrupt is triggered on CSRNG instantiate and
* reseed commands.
*/
static void test_csrng_sw_entropy_req_interrupt(void) {
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));
// Instantiate the CSRNG instance with no seed material to use the seed
// provided by entropy_src exclusively.
const dif_csrng_seed_material_t kEntropyInput = {0};
csrng_testutils_cmd_ready_wait(&csrng);
csrng_entropy_req_irq_enable();
CHECK_DIF_OK(dif_csrng_instantiate(&csrng, kDifCsrngEntropySrcToggleEnable,
&kEntropyInput));
csrng_entropy_req_irq_block_wait();
csrng_generate_output_check();
csrng_entropy_req_irq_enable();
CHECK_DIF_OK(dif_csrng_reseed(&csrng, &kEntropyInput));
csrng_entropy_req_irq_block_wait();
csrng_generate_output_check();
csrng_testutils_cmd_status_check(&csrng);
csrng_testutils_recoverable_alerts_check(&csrng);
}
/**
* Verifies that the entropy req interrupt is triggered on EDN instantiate and
* reseed commands.
*/
static void test_csrng_edn_entropy_req_interrupt(void) {
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));
const dif_edn_seed_material_t kEntropyInput = {0};
CHECK_DIF_OK(dif_edn_configure(&edn0));
csrng_entropy_req_irq_enable();
CHECK_DIF_OK(dif_edn_instantiate(&edn0, kDifEdnEntropySrcToggleEnable,
&kEntropyInput));
csrng_entropy_req_irq_block_wait();
csrng_entropy_req_irq_enable();
CHECK_DIF_OK(dif_edn_reseed(&edn0, &kEntropyInput));
csrng_entropy_req_irq_block_wait();
CHECK_DIF_OK(dif_edn_uninstantiate(&edn0));
CHECK_DIF_OK(dif_edn_configure(&edn1));
csrng_entropy_req_irq_enable();
CHECK_DIF_OK(dif_edn_instantiate(&edn1, kDifEdnEntropySrcToggleEnable,
&kEntropyInput));
csrng_entropy_req_irq_block_wait();
csrng_entropy_req_irq_enable();
CHECK_DIF_OK(dif_edn_reseed(&edn1, &kEntropyInput));
csrng_entropy_req_irq_block_wait();
CHECK_DIF_OK(dif_edn_uninstantiate(&edn1));
csrng_testutils_recoverable_alerts_check(&csrng);
}
void ottf_external_isr(void) {
top_earlgrey_plic_peripheral_t plic_peripheral;
dif_csrng_irq_t irq;
isr_testutils_csrng_isr(
(plic_isr_ctx_t){
.rv_plic = &plic,
.hart_id = kTopEarlgreyPlicTargetIbex0,
},
(csrng_isr_ctx_t){
.csrng = &csrng,
.plic_csrng_start_irq_id = kTopEarlgreyPlicIrqIdCsrngCsCmdReqDone,
.expected_irq = kDifCsrngIrqCsEntropyReq,
.is_only_irq = false,
},
&plic_peripheral, &irq);
CHECK(plic_peripheral == kTopEarlgreyPlicPeripheralCsrng,
"Interrupt from incorrect peripheral: (expected: %d, got: %d)",
kTopEarlgreyPlicPeripheralCsrng, plic_peripheral);
CHECK(irq == kDifCsrngIrqCsEntropyReq);
entropy_src_isr_csrng_req_set = true;
}
bool test_main(void) {
init_peripherals();
uint32_t num_iterations = kTestParamNumIterationsSim;
if (kDeviceType != kDeviceSimDV && kDeviceType != kDeviceSimVerilator) {
num_iterations = kTestParamNumIterationsOther;
}
for (size_t i = 0; i < num_iterations; ++i) {
test_csrng_sw_entropy_req_interrupt();
test_csrng_edn_entropy_req_interrupt();
}
return true;
}