blob: 93f94d2dda4f341a64b01dec96f52f19aec837b1 [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/dif/dif_csrng.h"
#include <array>
#include <vector>
#include "gtest/gtest.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/base/mock_mmio.h"
#include "sw/device/lib/dif/dif_test_base.h"
#include "csrng_regs.h" // Generated
namespace dif_csrng_unittest {
namespace {
using ::testing::ElementsAreArray;
class DifCsrngTest : public testing::Test, public mock_mmio::MmioTest {
protected:
const dif_csrng_t csrng_ = {.base_addr = dev().region()};
};
class ConfigTest : public DifCsrngTest {};
TEST_F(ConfigTest, NullArgs) {
EXPECT_DIF_BADARG(dif_csrng_configure(nullptr));
}
TEST_F(ConfigTest, ConfigOk) {
constexpr uint32_t exp =
kMultiBitBool4True | kMultiBitBool4True << 4 | kMultiBitBool4True << 8;
EXPECT_READ32(CSRNG_REGWEN_REG_OFFSET, 1);
EXPECT_WRITE32(CSRNG_CTRL_REG_OFFSET, exp);
EXPECT_DIF_OK(dif_csrng_configure(&csrng_));
}
TEST_F(ConfigTest, Locked) {
EXPECT_READ32(CSRNG_REGWEN_REG_OFFSET, 0);
EXPECT_EQ(dif_csrng_configure(&csrng_), kDifLocked);
}
class GetCmdInterfaceStatusTest : public DifCsrngTest {};
TEST_F(GetCmdInterfaceStatusTest, NullArgs) {
dif_csrng_cmd_status_t status;
EXPECT_DIF_BADARG(dif_csrng_get_cmd_interface_status(nullptr, &status));
EXPECT_DIF_BADARG(dif_csrng_get_cmd_interface_status(&csrng_, nullptr));
}
struct StatusTestCase {
bool cmd_ready;
bool cmd_status;
uint32_t err_codes;
dif_csrng_cmd_status_t expected_status;
};
template <typename... Ints>
uint32_t BitSet(Ints... bits) {
uint32_t x = 0;
auto ignored = {x |= 1 << static_cast<int>(bits)...};
(void)ignored;
return x;
}
class GetCmdInterfaceStatusTestAllParams
: public GetCmdInterfaceStatusTest,
public testing::WithParamInterface<StatusTestCase> {};
TEST_P(GetCmdInterfaceStatusTestAllParams, ValidConfigurationMode) {
const auto &test_param = GetParam();
dif_csrng_cmd_status_t status{};
EXPECT_READ32(CSRNG_SW_CMD_STS_REG_OFFSET,
{
{CSRNG_SW_CMD_STS_CMD_RDY_BIT, test_param.cmd_ready},
{CSRNG_SW_CMD_STS_CMD_STS_BIT, test_param.cmd_status},
});
if (test_param.expected_status.kind == kDifCsrngCmdStatusError) {
EXPECT_READ32(CSRNG_ERR_CODE_REG_OFFSET, test_param.err_codes);
}
EXPECT_DIF_OK(dif_csrng_get_cmd_interface_status(&csrng_, &status));
EXPECT_EQ(status.kind, test_param.expected_status.kind);
EXPECT_EQ(status.unhealthy_fifos, test_param.expected_status.unhealthy_fifos);
EXPECT_EQ(status.errors, test_param.expected_status.errors);
}
INSTANTIATE_TEST_SUITE_P(
GetCmdInterfaceStatusTestAllParams, GetCmdInterfaceStatusTestAllParams,
testing::Values(StatusTestCase{true, false, 0, {kDifCsrngCmdStatusReady}},
StatusTestCase{false, false, 0, {kDifCsrngCmdStatusBusy}},
StatusTestCase{true, true, 0, {kDifCsrngCmdStatusError}},
StatusTestCase{
false,
true,
BitSet(CSRNG_ERR_CODE_SFIFO_GENBITS_ERR_BIT,
CSRNG_ERR_CODE_SFIFO_PDATA_ERR_BIT,
CSRNG_ERR_CODE_AES_CIPHER_SM_ERR_BIT,
CSRNG_ERR_CODE_FIFO_STATE_ERR_BIT),
{
.kind = kDifCsrngCmdStatusError,
.unhealthy_fifos = BitSet(kDifCsrngFifoGenBits,
kDifCsrngFifoPData),
.errors = BitSet(kDifCsrngErrorAesSm,
kDifCsrngErrorFifoFullAndEmpty),
},
}));
class ForceErrorTest : public DifCsrngTest {};
TEST_F(ForceErrorTest, BadArgs) {
EXPECT_DIF_BADARG(dif_csrng_get_cmd_force_unhealthy_fifo(
&csrng_, static_cast<dif_csrng_fifo_t>(-1)));
EXPECT_DIF_BADARG(
dif_csrng_get_cmd_force_unhealthy_fifo(nullptr, kDifCsrngFifoGenBits));
EXPECT_DIF_BADARG(dif_csrng_get_cmd_force_error(
&csrng_, static_cast<dif_csrng_error_t>(-1)));
EXPECT_DIF_BADARG(
dif_csrng_get_cmd_force_error(nullptr, kDifCsrngErrorAesSm));
}
TEST_F(ForceErrorTest, ForceFifo) {
EXPECT_READ32(CSRNG_REGWEN_REG_OFFSET, 1);
EXPECT_WRITE32(CSRNG_ERR_CODE_TEST_REG_OFFSET,
CSRNG_ERR_CODE_SFIFO_GENBITS_ERR_BIT);
EXPECT_DIF_OK(
dif_csrng_get_cmd_force_unhealthy_fifo(&csrng_, kDifCsrngFifoGenBits));
}
TEST_F(ForceErrorTest, ForceError) {
EXPECT_READ32(CSRNG_REGWEN_REG_OFFSET, 1);
EXPECT_WRITE32(CSRNG_ERR_CODE_TEST_REG_OFFSET,
CSRNG_ERR_CODE_AES_CIPHER_SM_ERR_BIT);
EXPECT_DIF_OK(dif_csrng_get_cmd_force_error(&csrng_, kDifCsrngErrorAesSm));
}
TEST_F(ForceErrorTest, Locked) {
EXPECT_READ32(CSRNG_REGWEN_REG_OFFSET, 0);
EXPECT_READ32(CSRNG_REGWEN_REG_OFFSET, 0);
EXPECT_EQ(
dif_csrng_get_cmd_force_unhealthy_fifo(&csrng_, kDifCsrngFifoGenBits),
kDifLocked);
EXPECT_EQ(dif_csrng_get_cmd_force_error(&csrng_, kDifCsrngErrorAesSm),
kDifLocked);
}
class MiscStatusTest : public DifCsrngTest {};
TEST_F(MiscStatusTest, BadArgs) {
uint32_t out;
EXPECT_DIF_BADARG(dif_csrng_get_main_state_machine(nullptr, &out));
EXPECT_DIF_BADARG(dif_csrng_get_main_state_machine(&csrng_, nullptr));
EXPECT_DIF_BADARG(dif_csrng_get_hw_csrng_exceptions(nullptr, &out));
EXPECT_DIF_BADARG(dif_csrng_get_hw_csrng_exceptions(&csrng_, nullptr));
EXPECT_DIF_BADARG(dif_csrng_clear_hw_csrng_exceptions(nullptr));
}
TEST_F(MiscStatusTest, GetMainSm) {
EXPECT_READ32(CSRNG_MAIN_SM_STATE_REG_OFFSET, 42);
uint32_t out;
EXPECT_DIF_OK(dif_csrng_get_main_state_machine(&csrng_, &out));
EXPECT_EQ(out, 42);
}
TEST_F(MiscStatusTest, GetExceptions) {
EXPECT_READ32(CSRNG_HW_EXC_STS_REG_OFFSET, 42);
uint32_t out;
EXPECT_DIF_OK(dif_csrng_get_hw_csrng_exceptions(&csrng_, &out));
EXPECT_EQ(out, 42);
}
TEST_F(MiscStatusTest, ClearExceptions) {
EXPECT_WRITE32(CSRNG_HW_EXC_STS_REG_OFFSET, 0);
EXPECT_DIF_OK(dif_csrng_clear_hw_csrng_exceptions(&csrng_));
}
class GetOutputStatusTest : public DifCsrngTest {};
TEST_F(GetOutputStatusTest, NullArgs) {
dif_csrng_output_status_t status;
EXPECT_DIF_BADARG(dif_csrng_get_output_status(nullptr, &status));
EXPECT_DIF_BADARG(dif_csrng_get_output_status(&csrng_, nullptr));
}
TEST_F(GetOutputStatusTest, ValidStatus) {
// Each option is initialized to a boolean complement of the other so
// that we can test both fields toggling in one pass.
dif_csrng_output_status_t status = {.valid_data = false, .fips_mode = true};
EXPECT_READ32(CSRNG_GENBITS_VLD_REG_OFFSET,
{
{CSRNG_GENBITS_VLD_GENBITS_VLD_BIT, true},
{CSRNG_GENBITS_VLD_GENBITS_FIPS_BIT, false},
});
EXPECT_DIF_OK(dif_csrng_get_output_status(&csrng_, &status));
EXPECT_EQ(status.valid_data, true);
EXPECT_EQ(status.fips_mode, false);
}
/**
* DRBG commands are tested using this test group as the underlying
* command interface is shared across API functions.
*/
class CommandTest : public DifCsrngTest {
protected:
dif_csrng_seed_material_t seed_material_ = {
.seed_material_len = 0,
.seed_material = {0},
};
};
TEST_F(CommandTest, InstantiateOk) {
EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET,
0x00000001 | kMultiBitBool4True << 8);
EXPECT_DIF_OK(dif_csrng_instantiate(&csrng_, kDifCsrngEntropySrcToggleDisable,
&seed_material_));
EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET,
0x00000001 | kMultiBitBool4False << 8);
EXPECT_DIF_OK(dif_csrng_instantiate(&csrng_, kDifCsrngEntropySrcToggleEnable,
&seed_material_));
seed_material_.seed_material[0] = 0x5a5a5a5a;
seed_material_.seed_material_len = 1;
EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET,
0x00000011 | kMultiBitBool4False << 8);
EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x5a5a5a5a);
EXPECT_DIF_OK(dif_csrng_instantiate(&csrng_, kDifCsrngEntropySrcToggleEnable,
&seed_material_));
}
TEST_F(CommandTest, InstantiateBadArgs) {
EXPECT_DIF_BADARG(dif_csrng_instantiate(
nullptr, kDifCsrngEntropySrcToggleDisable, &seed_material_));
// Failed overflow check.
seed_material_.seed_material_len = 16;
EXPECT_DIF_BADARG(dif_csrng_instantiate(
&csrng_, kDifCsrngEntropySrcToggleDisable, &seed_material_));
}
TEST_F(CommandTest, ReseedOk) {
EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET,
0x00000002 | kMultiBitBool4False << 8);
EXPECT_DIF_OK(dif_csrng_reseed(&csrng_, &seed_material_));
seed_material_.seed_material[0] = 0x5a5a5a5a;
seed_material_.seed_material_len = 1;
EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET,
0x00000012 | kMultiBitBool4False << 8);
EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x5a5a5a5a);
EXPECT_DIF_OK(dif_csrng_reseed(&csrng_, &seed_material_));
}
TEST_F(CommandTest, ReseedBadArgs) {
EXPECT_DIF_BADARG(dif_csrng_reseed(nullptr, &seed_material_));
// Failed overflow check.
seed_material_.seed_material_len = 16;
EXPECT_DIF_BADARG(dif_csrng_reseed(&csrng_, &seed_material_));
}
TEST_F(CommandTest, UpdateOk) {
EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET,
0x00000004 | kMultiBitBool4False << 8);
EXPECT_DIF_OK(dif_csrng_update(&csrng_, &seed_material_));
seed_material_.seed_material[0] = 0x5a5a5a5a;
seed_material_.seed_material_len = 1;
EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET,
0x00000014 | kMultiBitBool4False << 8);
EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x5a5a5a5a);
EXPECT_DIF_OK(dif_csrng_update(&csrng_, &seed_material_));
}
TEST_F(CommandTest, UpdateBadArgs) {
EXPECT_DIF_BADARG(dif_csrng_update(nullptr, &seed_material_));
}
TEST_F(CommandTest, GenerateOk) {
// 512bits = 16 x 32bit = 4 x 128bit blocks
EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET,
0x00004003 | kMultiBitBool4False << 8);
EXPECT_DIF_OK(dif_csrng_generate_start(&csrng_, /*len=*/16));
// 576bits = 18 x 32bit = 5 x 128bit blocks (rounded up)
EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET,
0x00005003 | kMultiBitBool4False << 8);
EXPECT_DIF_OK(dif_csrng_generate_start(&csrng_, /*len=*/18));
}
TEST_F(CommandTest, GenerateBadArgs) {
EXPECT_DIF_BADARG(dif_csrng_generate_start(nullptr, /*len=*/1));
EXPECT_DIF_BADARG(dif_csrng_generate_start(&csrng_, /*len=*/0));
}
TEST_F(CommandTest, UninstantiateOk) {
EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET,
0x00000005 | kMultiBitBool4False << 8);
EXPECT_DIF_OK(dif_csrng_uninstantiate(&csrng_));
}
class GenerateEndTest : public DifCsrngTest {};
TEST_F(GenerateEndTest, ReadOk) {
constexpr std::array<uint32_t, 4> kExpected = {
0x00000000,
0x11111111,
0x22222222,
0x33333333,
};
EXPECT_READ32(CSRNG_GENBITS_VLD_REG_OFFSET,
{
{CSRNG_GENBITS_VLD_GENBITS_VLD_BIT, true},
});
for (const uint32_t val : kExpected) {
EXPECT_READ32(CSRNG_GENBITS_REG_OFFSET, val);
}
std::vector<uint32_t> got(kExpected.size());
EXPECT_DIF_OK(dif_csrng_generate_read(&csrng_, got.data(), got.size()));
EXPECT_THAT(got, testing::ElementsAreArray(kExpected));
}
TEST_F(GenerateEndTest, ReadBadArgs) {
EXPECT_DIF_BADARG(dif_csrng_generate_read(&csrng_, nullptr, /*len=*/0));
uint32_t data;
EXPECT_DIF_BADARG(dif_csrng_generate_read(nullptr, &data, /*len=*/1));
}
class GetInternalStateTest : public DifCsrngTest {};
TEST_F(GetInternalStateTest, GetInternalStateOk) {
dif_csrng_internal_state_id_t instance_id = kCsrngInternalStateIdSw;
EXPECT_WRITE32(CSRNG_INT_STATE_NUM_REG_OFFSET,
{
{CSRNG_INT_STATE_NUM_INT_STATE_NUM_OFFSET,
static_cast<uint32_t>(instance_id)},
});
dif_csrng_internal_state_t expected = {
.reseed_counter = 1,
.v = {1, 2, 3, 4},
.key = {1, 2, 3, 4, 5, 6, 7, 8},
.instantiated = true,
.fips_compliance = true,
};
EXPECT_READ32(CSRNG_INT_STATE_VAL_REG_OFFSET, expected.reseed_counter);
for (size_t i = 0; i < 4; ++i) {
EXPECT_READ32(CSRNG_INT_STATE_VAL_REG_OFFSET, expected.v[i]);
}
for (size_t i = 0; i < 8; ++i) {
EXPECT_READ32(CSRNG_INT_STATE_VAL_REG_OFFSET, expected.key[i]);
}
EXPECT_READ32(CSRNG_INT_STATE_VAL_REG_OFFSET, 3);
dif_csrng_internal_state_t got;
EXPECT_DIF_OK(dif_csrng_get_internal_state(&csrng_, instance_id, &got));
EXPECT_EQ(got.reseed_counter, expected.reseed_counter);
EXPECT_THAT(got.key, ElementsAreArray(expected.key));
EXPECT_THAT(got.v, ElementsAreArray(expected.v));
EXPECT_EQ(got.instantiated, expected.instantiated);
EXPECT_EQ(got.fips_compliance, expected.fips_compliance);
}
TEST_F(GetInternalStateTest, GetInternalStateBadArgs) {
EXPECT_DIF_BADARG(
dif_csrng_get_internal_state(&csrng_, kCsrngInternalStateIdSw, nullptr));
dif_csrng_internal_state unused;
EXPECT_DIF_BADARG(
dif_csrng_get_internal_state(nullptr, kCsrngInternalStateIdSw, &unused));
}
class LockTest : public DifCsrngTest {};
TEST_F(LockTest, BadArgs) {
bool flag;
EXPECT_DIF_BADARG(dif_csrng_lock(nullptr));
EXPECT_DIF_BADARG(dif_csrng_is_locked(nullptr, &flag));
EXPECT_DIF_BADARG(dif_csrng_is_locked(&csrng_, nullptr));
}
TEST_F(LockTest, Lock) {
EXPECT_WRITE32(CSRNG_REGWEN_REG_OFFSET, 0);
EXPECT_DIF_OK(dif_csrng_lock(&csrng_));
}
TEST_F(LockTest, IsLocked) {
bool flag;
EXPECT_READ32(CSRNG_REGWEN_REG_OFFSET, 1);
EXPECT_DIF_OK(dif_csrng_is_locked(&csrng_, &flag));
EXPECT_FALSE(flag);
EXPECT_READ32(CSRNG_REGWEN_REG_OFFSET, 0);
EXPECT_DIF_OK(dif_csrng_is_locked(&csrng_, &flag));
EXPECT_TRUE(flag);
}
class StopTest : public DifCsrngTest {};
TEST_F(StopTest, BadArgs) { EXPECT_DIF_BADARG(dif_csrng_stop(nullptr)); }
TEST_F(StopTest, Stop) {
EXPECT_READ32(CSRNG_REGWEN_REG_OFFSET, 1);
EXPECT_WRITE32(CSRNG_CTRL_REG_OFFSET, CSRNG_CTRL_REG_RESVAL);
EXPECT_DIF_OK(dif_csrng_stop(&csrng_));
}
TEST_F(StopTest, Locked) {
EXPECT_READ32(CSRNG_REGWEN_REG_OFFSET, 0);
EXPECT_EQ(dif_csrng_stop(&csrng_), kDifLocked);
}
class AlertTest : public DifCsrngTest {};
TEST_F(AlertTest, BadArgs) {
uint32_t out;
EXPECT_DIF_BADARG(dif_csrng_get_recoverable_alerts(nullptr, &out));
EXPECT_DIF_BADARG(dif_csrng_get_recoverable_alerts(&csrng_, nullptr));
EXPECT_DIF_BADARG(dif_csrng_clear_recoverable_alerts(nullptr));
}
TEST_F(AlertTest, Get) {
uint32_t out;
EXPECT_READ32(CSRNG_RECOV_ALERT_STS_REG_OFFSET,
{
{CSRNG_RECOV_ALERT_STS_ENABLE_FIELD_ALERT_BIT, true},
{CSRNG_RECOV_ALERT_STS_CS_BUS_CMP_ALERT_BIT, true},
});
EXPECT_DIF_OK(dif_csrng_get_recoverable_alerts(&csrng_, &out));
EXPECT_EQ(out, BitSet(kDifCsrngRecoverableAlertBadEnable,
kDifCsrngRecoverableAlertRepeatedGenBits));
}
TEST_F(AlertTest, Clear) {
EXPECT_WRITE32(CSRNG_RECOV_ALERT_STS_REG_OFFSET, 0);
EXPECT_DIF_OK(dif_csrng_clear_recoverable_alerts(&csrng_));
}
} // namespace
} // namespace dif_csrng_unittest