[dif/edn] Add unit tests

Signed-off-by: Miguel Young de la Sota <mcyoung@google.com>
diff --git a/sw/device/lib/dif/BUILD b/sw/device/lib/dif/BUILD
index acf3e79..038d957 100644
--- a/sw/device/lib/dif/BUILD
+++ b/sw/device/lib/dif/BUILD
@@ -273,6 +273,7 @@
     name = "edn_unittest",
     srcs = [
         "autogen/dif_edn_autogen_unittest.cc",
+        "dif_edn_unittest.cc",
     ],
     deps = [
         ":edn",
diff --git a/sw/device/lib/dif/dif_edn.c b/sw/device/lib/dif/dif_edn.c
index 9222f7f..f61b243 100644
--- a/sw/device/lib/dif/dif_edn.c
+++ b/sw/device/lib/dif/dif_edn.c
@@ -161,6 +161,7 @@
       break;
     case kDifEdnFifoGenerateCmd:
       fifo_bit = EDN_ERR_CODE_SFIFO_GENCMD_ERR_BIT;
+      break;
     default:
       return kDifBadArg;
   }
diff --git a/sw/device/lib/dif/dif_edn_unittest.cc b/sw/device/lib/dif/dif_edn_unittest.cc
new file mode 100644
index 0000000..8f4b148
--- /dev/null
+++ b/sw/device/lib/dif/dif_edn_unittest.cc
@@ -0,0 +1,373 @@
+// 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_edn.h"
+
+#include <array>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "sw/device/lib/base/mmio.h"
+#include "sw/device/lib/base/testing/mock_mmio.h"
+#include "sw/device/lib/dif/dif_test_base.h"
+
+#include "edn_regs.h"  // Generated
+
+namespace dif_edn_unittest {
+namespace {
+
+using ::testing::ElementsAreArray;
+
+class DifEdnTest : public testing::Test, public mock_mmio::MmioTest {
+ protected:
+  const dif_edn_t edn_ = {.base_addr = dev().region()};
+};
+
+class ConfigTest : public DifEdnTest {};
+
+TEST_F(ConfigTest, BadArgs) { EXPECT_DIF_BADARG(dif_edn_configure(nullptr)); }
+
+TEST_F(ConfigTest, ConfigOk) {
+  EXPECT_READ32(EDN_REGWEN_REG_OFFSET, 1);
+  EXPECT_MASK32(EDN_CTRL_REG_OFFSET,
+                {
+                    {EDN_CTRL_EDN_ENABLE_OFFSET, 0xf, kMultiBitBool4True},
+                });
+  EXPECT_DIF_OK(dif_edn_configure(&edn_));
+}
+
+TEST_F(ConfigTest, Locked) {
+  EXPECT_READ32(EDN_REGWEN_REG_OFFSET, 0);
+  EXPECT_EQ(dif_edn_configure(&edn_), kDifLocked);
+}
+
+class LockTest : public DifEdnTest {};
+
+TEST_F(LockTest, BadArgs) {
+  bool flag;
+  EXPECT_DIF_BADARG(dif_edn_lock(nullptr));
+  EXPECT_DIF_BADARG(dif_edn_is_locked(nullptr, &flag));
+  EXPECT_DIF_BADARG(dif_edn_is_locked(&edn_, nullptr));
+}
+
+TEST_F(LockTest, Lock) {
+  EXPECT_WRITE32(EDN_REGWEN_REG_OFFSET, 0);
+  EXPECT_DIF_OK(dif_edn_lock(&edn_));
+}
+
+TEST_F(LockTest, IsLocked) {
+  bool flag;
+
+  EXPECT_READ32(EDN_REGWEN_REG_OFFSET, 1);
+  EXPECT_DIF_OK(dif_edn_is_locked(&edn_, &flag));
+  EXPECT_FALSE(flag);
+
+  EXPECT_READ32(EDN_REGWEN_REG_OFFSET, 0);
+  EXPECT_DIF_OK(dif_edn_is_locked(&edn_, &flag));
+  EXPECT_TRUE(flag);
+}
+
+class SetModeTest : public DifEdnTest {};
+
+TEST_F(SetModeTest, BadArgs) {
+  EXPECT_DIF_BADARG(dif_edn_set_boot_mode(nullptr));
+  EXPECT_DIF_BADARG(dif_edn_set_auto_mode(nullptr, {}));
+}
+
+TEST_F(SetModeTest, Boot) {
+  EXPECT_READ32(EDN_REGWEN_REG_OFFSET, 1);
+  EXPECT_MASK32(EDN_CTRL_REG_OFFSET,
+                {
+                    {EDN_CTRL_BOOT_REQ_MODE_OFFSET, 0xf, kMultiBitBool4True},
+                });
+  EXPECT_DIF_OK(dif_edn_set_boot_mode(&edn_));
+}
+
+TEST_F(SetModeTest, Auto) {
+  EXPECT_READ32(EDN_REGWEN_REG_OFFSET, 1);
+
+  EXPECT_READ32(EDN_CTRL_REG_OFFSET, EDN_CTRL_REG_RESVAL);
+  EXPECT_WRITE32(EDN_CTRL_REG_OFFSET,
+                 {
+                     {EDN_CTRL_EDN_ENABLE_OFFSET, kMultiBitBool4False},
+                     {EDN_CTRL_BOOT_REQ_MODE_OFFSET, kMultiBitBool4False},
+                     {EDN_CTRL_AUTO_REQ_MODE_OFFSET, kMultiBitBool4False},
+                     {EDN_CTRL_CMD_FIFO_RST_OFFSET, kMultiBitBool4True},
+                 });
+
+  dif_edn_auto_params_t params = {
+      .reseed_material =
+          {
+              .len = 5,
+              .data = {1, 2, 3, 4, 5},
+          },
+      .generate_material =
+          {
+              .len = 7,
+              .data = {6, 7, 8, 9, 10, 11, 12},
+          },
+      .reseed_interval = 42,
+  };
+
+  for (size_t i = 0; i < params.reseed_material.len; ++i) {
+    EXPECT_WRITE32(EDN_RESEED_CMD_REG_OFFSET, params.reseed_material.data[i]);
+  }
+  for (size_t i = 0; i < params.generate_material.len; ++i) {
+    EXPECT_WRITE32(EDN_GENERATE_CMD_REG_OFFSET,
+                   params.generate_material.data[i]);
+  }
+  EXPECT_WRITE32(EDN_MAX_NUM_REQS_BETWEEN_RESEEDS_REG_OFFSET,
+                 params.reseed_interval);
+
+  EXPECT_WRITE32(EDN_CTRL_REG_OFFSET,
+                 {
+                     {EDN_CTRL_EDN_ENABLE_OFFSET, kMultiBitBool4False},
+                     {EDN_CTRL_BOOT_REQ_MODE_OFFSET, kMultiBitBool4False},
+                     {EDN_CTRL_AUTO_REQ_MODE_OFFSET, kMultiBitBool4True},
+                     {EDN_CTRL_CMD_FIFO_RST_OFFSET, kMultiBitBool4True},
+                 });
+
+  EXPECT_DIF_OK(dif_edn_set_auto_mode(&edn_, params));
+}
+
+TEST_F(SetModeTest, Locked) {
+  EXPECT_READ32(EDN_REGWEN_REG_OFFSET, 0);
+  EXPECT_READ32(EDN_REGWEN_REG_OFFSET, 0);
+  EXPECT_EQ(dif_edn_set_boot_mode(&edn_), kDifLocked);
+  EXPECT_EQ(dif_edn_set_auto_mode(&edn_, {}), kDifLocked);
+}
+
+class GetStatusTest : public DifEdnTest {};
+
+TEST_F(GetStatusTest, BadArgs) {
+  bool flag;
+  EXPECT_DIF_BADARG(dif_edn_get_status(nullptr, kDifEdnStatusReady, &flag));
+  EXPECT_DIF_BADARG(
+      dif_edn_get_status(&edn_, static_cast<dif_edn_status_t>(-1), &flag));
+  EXPECT_DIF_BADARG(dif_edn_get_status(&edn_, kDifEdnStatusReady, nullptr));
+}
+
+TEST_F(GetStatusTest, Ok) {
+  bool flag;
+
+  EXPECT_READ32(EDN_SW_CMD_STS_REG_OFFSET,
+                {
+                    {EDN_SW_CMD_STS_CMD_RDY_BIT, true},
+                    {EDN_SW_CMD_STS_CMD_STS_BIT, false},
+                });
+  EXPECT_DIF_OK(dif_edn_get_status(&edn_, kDifEdnStatusReady, &flag));
+  EXPECT_TRUE(flag);
+
+  EXPECT_READ32(EDN_SW_CMD_STS_REG_OFFSET,
+                {
+                    {EDN_SW_CMD_STS_CMD_RDY_BIT, true},
+                    {EDN_SW_CMD_STS_CMD_STS_BIT, false},
+                });
+  EXPECT_DIF_OK(dif_edn_get_status(&edn_, kDifEdnStatusCsrngAck, &flag));
+  EXPECT_FALSE(flag);
+}
+
+class ErrorTest : public DifEdnTest {};
+
+TEST_F(ErrorTest, BadArgs) {
+  uint32_t set;
+  EXPECT_DIF_BADARG(dif_edn_get_errors(nullptr, &set, &set));
+  EXPECT_DIF_BADARG(dif_edn_get_errors(&edn_, nullptr, &set));
+  EXPECT_DIF_BADARG(dif_edn_get_errors(&edn_, &set, nullptr));
+  EXPECT_DIF_BADARG(dif_edn_get_cmd_unhealthy_fifo_force(
+      &edn_, static_cast<dif_edn_fifo_t>(-1)));
+  EXPECT_DIF_BADARG(
+      dif_edn_get_cmd_unhealthy_fifo_force(nullptr, kDifEdnFifoReseedCmd));
+  EXPECT_DIF_BADARG(
+      dif_edn_get_cmd_error_force(&edn_, static_cast<dif_edn_error_t>(-1)));
+  EXPECT_DIF_BADARG(dif_edn_get_cmd_error_force(nullptr, kDifEdnErrorMainSm));
+}
+
+TEST_F(ErrorTest, Ok) {
+  EXPECT_READ32(EDN_ERR_CODE_REG_OFFSET,
+                {
+                    {EDN_ERR_CODE_SFIFO_RESCMD_ERR_BIT, true},
+                    {EDN_ERR_CODE_EDN_MAIN_SM_ERR_BIT, true},
+                    {EDN_ERR_CODE_FIFO_STATE_ERR_BIT, true},
+                });
+
+  uint32_t fifos, errors;
+  EXPECT_DIF_OK(dif_edn_get_errors(&edn_, &fifos, &errors));
+  EXPECT_EQ(fifos, 1 << kDifEdnFifoReseedCmd);
+  EXPECT_EQ(errors,
+            1 << kDifEdnErrorMainSm | 1 << kDifEdnErrorFifoFullAndEmpty);
+}
+
+TEST_F(ErrorTest, ForceFifo) {
+  EXPECT_READ32(EDN_REGWEN_REG_OFFSET, 1);
+  EXPECT_WRITE32(EDN_ERR_CODE_TEST_REG_OFFSET,
+                 EDN_ERR_CODE_SFIFO_RESCMD_ERR_BIT);
+  EXPECT_DIF_OK(
+      dif_edn_get_cmd_unhealthy_fifo_force(&edn_, kDifEdnFifoReseedCmd));
+}
+
+TEST_F(ErrorTest, ForceError) {
+  EXPECT_READ32(EDN_REGWEN_REG_OFFSET, 1);
+  EXPECT_WRITE32(EDN_ERR_CODE_TEST_REG_OFFSET,
+                 EDN_ERR_CODE_EDN_MAIN_SM_ERR_BIT);
+  EXPECT_DIF_OK(dif_edn_get_cmd_error_force(&edn_, kDifEdnErrorMainSm));
+}
+
+TEST_F(ErrorTest, Locked) {
+  EXPECT_READ32(EDN_REGWEN_REG_OFFSET, 0);
+  EXPECT_READ32(EDN_REGWEN_REG_OFFSET, 0);
+  EXPECT_EQ(dif_edn_get_cmd_unhealthy_fifo_force(&edn_, kDifEdnFifoReseedCmd),
+            kDifLocked);
+  EXPECT_EQ(dif_edn_get_cmd_error_force(&edn_, kDifEdnErrorMainSm), kDifLocked);
+}
+
+class MiscStatusTest : public DifEdnTest {};
+
+TEST_F(MiscStatusTest, BadArgs) {
+  uint32_t out;
+  EXPECT_DIF_BADARG(dif_edn_get_main_state_machine(nullptr, &out));
+  EXPECT_DIF_BADARG(dif_edn_get_main_state_machine(&edn_, nullptr));
+}
+
+TEST_F(MiscStatusTest, GetMainSm) {
+  EXPECT_READ32(EDN_MAIN_SM_STATE_REG_OFFSET, 42);
+
+  uint32_t out;
+  EXPECT_DIF_OK(dif_edn_get_main_state_machine(&edn_, &out));
+  EXPECT_EQ(out, 42);
+}
+
+/**
+ * DRBG commands are tested using this test group as the underlying
+ * command interface is shared across API functions.
+ */
+class CommandTest : public DifEdnTest {
+ protected:
+  dif_edn_seed_material_t seed_material_{};
+};
+
+TEST_F(CommandTest, InstantiateOk) {
+  EXPECT_WRITE32(EDN_SW_CMD_REQ_REG_OFFSET, 0x00000101);
+  EXPECT_DIF_OK(dif_edn_instantiate(&edn_, kDifEdnEntropySrcToggleDisable,
+                                    &seed_material_));
+
+  EXPECT_WRITE32(EDN_SW_CMD_REQ_REG_OFFSET, 0x00000001);
+  EXPECT_DIF_OK(dif_edn_instantiate(&edn_, kDifEdnEntropySrcToggleEnable,
+                                    &seed_material_));
+
+  seed_material_.data[0] = 0x5a5a5a5a;
+  seed_material_.len = 1;
+  EXPECT_WRITE32(EDN_SW_CMD_REQ_REG_OFFSET, 0x00000011);
+  EXPECT_WRITE32(EDN_SW_CMD_REQ_REG_OFFSET, 0x5a5a5a5a);
+  EXPECT_DIF_OK(dif_edn_instantiate(&edn_, kDifEdnEntropySrcToggleEnable,
+                                    &seed_material_));
+}
+
+TEST_F(CommandTest, InstantiateBadArgs) {
+  EXPECT_DIF_BADARG(dif_edn_instantiate(nullptr, kDifEdnEntropySrcToggleDisable,
+                                        &seed_material_));
+
+  // Failed overflow check.
+  seed_material_.len = 16;
+  EXPECT_DIF_BADARG(dif_edn_instantiate(&edn_, kDifEdnEntropySrcToggleDisable,
+                                        &seed_material_));
+}
+
+TEST_F(CommandTest, ReseedOk) {
+  EXPECT_WRITE32(EDN_SW_CMD_REQ_REG_OFFSET, 0x00000002);
+  EXPECT_DIF_OK(dif_edn_reseed(&edn_, &seed_material_));
+
+  seed_material_.data[0] = 0x5a5a5a5a;
+  seed_material_.len = 1;
+  EXPECT_WRITE32(EDN_SW_CMD_REQ_REG_OFFSET, 0x00000012);
+  EXPECT_WRITE32(EDN_SW_CMD_REQ_REG_OFFSET, 0x5a5a5a5a);
+  EXPECT_DIF_OK(dif_edn_reseed(&edn_, &seed_material_));
+}
+
+TEST_F(CommandTest, ReseedBadArgs) {
+  EXPECT_DIF_BADARG(dif_edn_reseed(nullptr, &seed_material_));
+
+  // Failed overflow check.
+  seed_material_.len = 16;
+  EXPECT_DIF_BADARG(dif_edn_reseed(&edn_, &seed_material_));
+}
+
+TEST_F(CommandTest, UpdateOk) {
+  EXPECT_WRITE32(EDN_SW_CMD_REQ_REG_OFFSET, 0x00000004);
+  EXPECT_DIF_OK(dif_edn_update(&edn_, &seed_material_));
+
+  seed_material_.data[0] = 0x5a5a5a5a;
+  seed_material_.len = 1;
+  EXPECT_WRITE32(EDN_SW_CMD_REQ_REG_OFFSET, 0x00000014);
+  EXPECT_WRITE32(EDN_SW_CMD_REQ_REG_OFFSET, 0x5a5a5a5a);
+  EXPECT_DIF_OK(dif_edn_update(&edn_, &seed_material_));
+}
+
+TEST_F(CommandTest, UpdateBadArgs) {
+  EXPECT_DIF_BADARG(dif_edn_update(nullptr, &seed_material_));
+}
+
+TEST_F(CommandTest, GenerateOk) {
+  // 512bits = 16 x 32bit = 4 x 128bit blocks
+  EXPECT_WRITE32(EDN_SW_CMD_REQ_REG_OFFSET, 0x00004003);
+  EXPECT_DIF_OK(dif_edn_generate_start(&edn_, /*len=*/16));
+
+  // 576bits = 18 x 32bit = 5 x 128bit blocks (rounded up)
+  EXPECT_WRITE32(EDN_SW_CMD_REQ_REG_OFFSET, 0x00005003);
+  EXPECT_DIF_OK(dif_edn_generate_start(&edn_, /*len=*/18));
+}
+
+TEST_F(CommandTest, GenerateBadArgs) {
+  EXPECT_DIF_BADARG(dif_edn_generate_start(nullptr, /*len=*/1));
+  EXPECT_DIF_BADARG(dif_edn_generate_start(&edn_, /*len=*/0));
+}
+
+TEST_F(CommandTest, UninstantiateOk) {
+  EXPECT_WRITE32(EDN_SW_CMD_REQ_REG_OFFSET, 0x00000005);
+  EXPECT_DIF_OK(dif_edn_uninstantiate(&edn_));
+}
+
+class StopTest : public DifEdnTest {};
+
+TEST_F(StopTest, BadArgs) { EXPECT_DIF_BADARG(dif_edn_stop(nullptr)); }
+
+TEST_F(StopTest, Stop) {
+  EXPECT_READ32(EDN_REGWEN_REG_OFFSET, 1);
+  EXPECT_WRITE32(EDN_CTRL_REG_OFFSET, EDN_CTRL_REG_RESVAL);
+  EXPECT_DIF_OK(dif_edn_stop(&edn_));
+}
+
+TEST_F(StopTest, Locked) {
+  EXPECT_READ32(EDN_REGWEN_REG_OFFSET, 0);
+  EXPECT_EQ(dif_edn_stop(&edn_), kDifLocked);
+}
+
+class AlertTest : public DifEdnTest {};
+
+TEST_F(AlertTest, BadArgs) {
+  uint32_t out;
+  EXPECT_DIF_BADARG(dif_edn_get_recoverable_alerts(nullptr, &out));
+  EXPECT_DIF_BADARG(dif_edn_get_recoverable_alerts(&edn_, nullptr));
+  EXPECT_DIF_BADARG(dif_edn_clear_recoverable_alerts(nullptr));
+}
+
+TEST_F(AlertTest, Get) {
+  uint32_t out;
+  EXPECT_READ32(EDN_RECOV_ALERT_STS_REG_OFFSET,
+                {
+                    {EDN_RECOV_ALERT_STS_BOOT_REQ_MODE_FIELD_ALERT_BIT, true},
+                    {EDN_RECOV_ALERT_STS_EDN_BUS_CMP_ALERT_BIT, true},
+                });
+  EXPECT_DIF_OK(dif_edn_get_recoverable_alerts(&edn_, &out));
+  EXPECT_EQ(out, 1 << kDifEdnRecoverableAlertBadBootReqMode |
+                     1 << kDifEdnRecoverableAlertRepeatedGenBits);
+}
+
+TEST_F(AlertTest, Clear) {
+  EXPECT_WRITE32(EDN_RECOV_ALERT_STS_REG_OFFSET, 0);
+  EXPECT_DIF_OK(dif_edn_clear_recoverable_alerts(&edn_));
+}
+
+}  // namespace
+}  // namespace dif_edn_unittest
diff --git a/sw/device/lib/dif/meson.build b/sw/device/lib/dif/meson.build
index 851d9d9..e821045 100644
--- a/sw/device/lib/dif/meson.build
+++ b/sw/device/lib/dif/meson.build
@@ -93,6 +93,7 @@
     sources: [
       hw_ip_csrng_reg_h,
       'dif_csrng.c',
+      'dif_csrng_shared.c',
     ],
     dependencies: [
       sw_lib_mmio,
@@ -101,26 +102,6 @@
   )
 )
 
-test('dif_csrng_unittest', executable(
-    'dif_csrng_unittest',
-    sources: [
-      'dif_csrng_unittest.cc',
-      'autogen/dif_csrng_autogen_unittest.cc',
-      meson.project_source_root() / 'sw/device/lib/dif/dif_csrng.c',
-      meson.project_source_root() / 'sw/device/lib/dif/autogen/dif_csrng_autogen.c',
-      hw_ip_csrng_reg_h,
-    ],
-    dependencies: [
-      sw_vendor_gtest,
-      sw_lib_base_testing_mock_mmio,
-    ],
-    native: true,
-    c_args: ['-DMOCK_MMIO'],
-    cpp_args: ['-DMOCK_MMIO'],
-  ),
-  suite: 'dif',
-)
-
 # EDN DIF Library (dif_csrng)
 sw_lib_dif_edn = declare_dependency(
   link_with: static_library(
@@ -136,25 +117,6 @@
   )
 )
 
-test('dif_edn_unittest', executable(
-    'dif_edn_unittest',
-    sources: [
-      'autogen/dif_edn_autogen_unittest.cc',
-      meson.project_source_root() / 'sw/device/lib/dif/dif_edn.c',
-      meson.project_source_root() / 'sw/device/lib/dif/autogen/dif_edn_autogen.c',
-      hw_ip_edn_reg_h,
-    ],
-    dependencies: [
-      sw_vendor_gtest,
-      sw_lib_base_testing_mock_mmio,
-    ],
-    native: true,
-    c_args: ['-DMOCK_MMIO'],
-    cpp_args: ['-DMOCK_MMIO'],
-  ),
-  suite: 'dif',
-)
-
 # UART DIF library (dif_uart)
 sw_lib_dif_uart = declare_dependency(
   link_with: static_library(