[sw/dif] CSRNG Updates

Add implementations for the following functions

*   `dif_csrng_instantiate()`
*   `dif_csrng_reseed()`
*   `dif_csrng_update()`
*   `dif_csrng_generate()`
*   `dif_csrng_read_output()`

Update `dif_csrng_smoketest.cc` to run the CSRNG in software mode
with a determistic seed.

Signed-off-by: Miguel Osorio <miguelosorio@google.com>
diff --git a/sw/device/lib/dif/dif_csrng.c b/sw/device/lib/dif/dif_csrng.c
index 5743986..00067e8 100644
--- a/sw/device/lib/dif/dif_csrng.c
+++ b/sw/device/lib/dif/dif_csrng.c
@@ -9,6 +9,83 @@
 
 #include "csrng_regs.h"  // Generated
 
+/**
+ * Supported CSRNG application commands.
+ * See https://docs.opentitan.org/hw/ip/csrng/doc/#command-header for
+ * details.
+ */
+typedef enum csrng_app_cmd_id {
+  kCsrngAppCmdInstantiate = 1,
+  kCsrngAppCmdReseed = 2,
+  kCsrngAppCmdGenerate = 3,
+  kCsrngAppCmdUpdate = 4,
+  kCsrngAppCmdUnisntantiate = 5,
+} csrng_app_cmd_id_t;
+
+/**
+ * CSRNG application interface command header parameters.
+ */
+typedef struct csrng_app_cmd {
+  /**
+   * Application command.
+   */
+  csrng_app_cmd_id_t id;
+  /**
+   * Entropy source enable.
+   *
+   * Mapped to flag0 in the hardware command interface.
+   */
+  dif_csrng_entropy_src_toggle_t entropy_src_enable;
+  /**
+   * Seed material. Only used in `kCsrngAppCmdInstantiate`, `kCsrngAppCmdReseed`
+   * and `kCsrngAppCmdUpdate` commands.
+   */
+  const dif_csrng_seed_material_t *seed_material;
+  /**
+   * Generate length. Specified as number of 128bit blocks.
+   */
+  uint32_t generate_len;
+} csrng_app_cmd_t;
+
+/**
+ * Writes application command `cmd` to the CSRNG_CMD_REQ_REG register.
+ * Returns the result of the operation.
+ */
+static dif_csrng_result_t write_application_command(
+    const dif_csrng_t *csrng, const csrng_app_cmd_t *cmd) {
+  if (csrng == NULL || cmd == NULL) {
+    return kDifCsrngBadArg;
+  }
+
+  // The application command header is not specified as a register in the
+  // hardware specification, so the fields are mapped here by hand. The
+  // command register also accepts arbitrary 32bit data.
+  const uint32_t kAppCmdBitFlag0 = 8;
+  const bitfield_field32_t kAppCmdFieldCmdId = {.mask = 0xf, .index = 0};
+  const bitfield_field32_t kAppCmdFieldCmdLen = {.mask = 0xf, .index = 4};
+  const bitfield_field32_t kAppCmdFieldGlen = {.mask = 0x7ffff, .index = 12};
+
+  uint32_t cmd_len =
+      cmd->seed_material == NULL ? 0 : cmd->seed_material->seed_material_len;
+
+  if (cmd_len & ~kAppCmdFieldCmdLen.mask) {
+    return kDifCsrngBadArg;
+  }
+
+  // Build and write application command header.
+  uint32_t reg = bitfield_field32_write(0, kAppCmdFieldCmdId, cmd->id);
+  reg = bitfield_field32_write(reg, kAppCmdFieldCmdLen, cmd_len);
+  reg = bitfield_bit32_write(reg, kAppCmdBitFlag0, cmd->entropy_src_enable);
+  reg = bitfield_field32_write(reg, kAppCmdFieldGlen, cmd->generate_len);
+  mmio_region_write32(csrng->params.base_addr, CSRNG_CMD_REQ_REG_OFFSET, reg);
+
+  for (uint32_t i = 0; i < cmd_len; ++i) {
+    mmio_region_write32(csrng->params.base_addr, CSRNG_CMD_REQ_REG_OFFSET,
+                        cmd->seed_material->seed_material[i]);
+  }
+  return kDifCsrngOk;
+}
+
 dif_csrng_result_t dif_csrng_init(dif_csrng_params_t params,
                                   dif_csrng_t *csrng) {
   if (csrng == NULL) {
@@ -36,6 +113,68 @@
   return kDifCsrngOk;
 }
 
+dif_csrng_result_t dif_csrng_instantiate(
+    const dif_csrng_t *csrng, dif_csrng_entropy_src_toggle_t entropy_src_enable,
+    const dif_csrng_seed_material_t *seed_material) {
+  const csrng_app_cmd_t app_cmd = {
+      .id = kCsrngAppCmdInstantiate,
+      .entropy_src_enable = entropy_src_enable,
+      .seed_material = seed_material,
+      .generate_len = 0,
+  };
+  return write_application_command(csrng, &app_cmd);
+}
+
+dif_csrng_result_t dif_csrng_reseed(
+    const dif_csrng_t *csrng, const dif_csrng_seed_material_t *seed_material) {
+  const csrng_app_cmd_t app_cmd = {
+      .id = kCsrngAppCmdReseed,
+      .entropy_src_enable = false,
+      .seed_material = seed_material,
+      .generate_len = 0,
+  };
+  return write_application_command(csrng, &app_cmd);
+}
+
+dif_csrng_result_t dif_csrng_update(
+    const dif_csrng_t *csrng, const dif_csrng_seed_material_t *seed_material) {
+  const csrng_app_cmd_t app_cmd = {
+      .id = kCsrngAppCmdUpdate,
+      .entropy_src_enable = false,
+      .seed_material = seed_material,
+      .generate_len = 0,
+  };
+  return write_application_command(csrng, &app_cmd);
+}
+
+dif_csrng_result_t dif_csrng_generate(const dif_csrng_t *csrng, size_t len) {
+  if (len == 0) {
+    return kDifCsrngBadArg;
+  }
+
+  // Round up the number of 128bit blocks. Aligning with respect to uint32_t.
+  // TODO(#6112): Consider using a canonical reference for alignment operations.
+  const uint32_t num_128bit_blocks = (len + 3) / 4;
+
+  const csrng_app_cmd_t app_cmd = {
+      .id = kCsrngAppCmdGenerate,
+      .entropy_src_enable = false,
+      .seed_material = NULL,
+      .generate_len = num_128bit_blocks,
+  };
+  return write_application_command(csrng, &app_cmd);
+}
+
+dif_csrng_result_t dif_csrng_uninstantiate(const dif_csrng_t *csrng) {
+  const csrng_app_cmd_t app_cmd = {
+      .id = kCsrngAppCmdUnisntantiate,
+      .entropy_src_enable = false,
+      .seed_material = NULL,
+      .generate_len = 0,
+  };
+  return write_application_command(csrng, &app_cmd);
+}
+
 dif_csrng_result_t dif_csrng_get_cmd_interface_status(
     const dif_csrng_t *csrng, dif_csrng_cmd_status_t *status) {
   if (csrng == NULL || status == NULL) {
@@ -76,3 +215,16 @@
       bitfield_bit32_read(reg, CSRNG_GENBITS_VLD_GENBITS_FIPS_BIT);
   return kDifCsrngOk;
 }
+
+dif_csrng_result_t dif_csrng_read_output(const dif_csrng_t *csrng,
+                                         uint32_t *buf, size_t len) {
+  if (csrng == NULL || buf == NULL) {
+    return kDifCsrngBadArg;
+  }
+
+  for (uint32_t i = 0; i < len; ++i) {
+    buf[i] =
+        mmio_region_read32(csrng->params.base_addr, CSRNG_GENBITS_REG_OFFSET);
+  }
+  return kDifCsrngOk;
+}
diff --git a/sw/device/lib/dif/dif_csrng.h b/sw/device/lib/dif/dif_csrng.h
index 24128c2..0b9eda2 100644
--- a/sw/device/lib/dif/dif_csrng.h
+++ b/sw/device/lib/dif/dif_csrng.h
@@ -199,11 +199,11 @@
    * Note: Software may opt to XOR the seed material with a seed to implement
    * a software assisted FIPS mode of operation.
    */
-  kDifCsrngEntropySrcToggleDisable,
+  kDifCsrngEntropySrcToggleDisable = 1,
   /**
    * Entropy source XOR'ed with the provided seed material.
    */
-  kDifCsrngEntropySrcToggleEnable,
+  kDifCsrngEntropySrcToggleEnable = 0,
 } dif_csrng_entropy_src_toggle_t;
 
 /**
diff --git a/sw/device/tests/dif/dif_csrng_smoketest.c b/sw/device/tests/dif/dif_csrng_smoketest.c
index 1b6c7ae..4922660 100644
--- a/sw/device/tests/dif/dif_csrng_smoketest.c
+++ b/sw/device/tests/dif/dif_csrng_smoketest.c
@@ -13,6 +13,88 @@
 
 const test_config_t kTestConfig;
 
+/**
+ * FIPS CAVP test vector for CTR DRBG.
+ *
+ * CAVP test parameters.
+ * Cipher: AES-256
+ * Derivation Function: No
+ * Prediction Resistance: False
+ */
+const dif_csrng_seed_material_t kEntropyInput = {
+    .seed_material =
+        {
+            0xe4bc23c5,
+            0x089a19d8,
+            0x6f4119cb,
+            0x3fa08c0a,
+            0x4991e0a1,
+            0xdef17e10,
+            0x1e4c14d9,
+            0xc323460a,
+            0x7c2fb58e,
+            0x0b086c6c,
+            0x57b55f56,
+            0xcae25bad,
+        },
+    .seed_material_len = 12,
+};
+
+/**
+ * Expected CSRNG output.
+ */
+const uint32_t kExpectedOutput[] = {
+    0xb2cb8905, 0xc05e5950, 0xca318950, 0x96be29ea, 0x3d5a3b82, 0xb2694955,
+    0x54eb80fe, 0x07de43e1, 0x93b9e7c3, 0xece73b80, 0xe062b1c1, 0xf68202fb,
+    0xb1c52a04, 0x0ea24788, 0x64295282, 0x234aaada,
+};
+const uint32_t kExpectedOutputLen = 16;
+
+/**
+ * Wait for the `csrng` instance command interface to be ready to
+ * accept commands. Aborts test execution if an error is found.
+ */
+static void wait_for_csrng_cmd_ready(const dif_csrng_t *csrng) {
+  dif_csrng_cmd_status_t cmd_status = {0};
+  while (cmd_status != kDifCsrngCmdStatusReady) {
+    CHECK(dif_csrng_get_cmd_interface_status(csrng, &cmd_status) ==
+          kDifCsrngOk);
+    CHECK(cmd_status != kDifCsrngCmdStatusError);
+  }
+}
+
+/**
+ * Run CAVP CTR DRBG Counter=0 with `kEntropyInput` deterministic
+ * seed material.
+ */
+void test_ctr_drbg_ctr0(const dif_csrng_t *csrng) {
+  wait_for_csrng_cmd_ready(csrng);
+  CHECK(dif_csrng_instantiate(csrng, kDifCsrngEntropySrcToggleDisable,
+                              &kEntropyInput) == kDifCsrngOk);
+
+  wait_for_csrng_cmd_ready(csrng);
+  const dif_csrng_seed_material_t seed_material = {0};
+  CHECK(dif_csrng_reseed(csrng, &seed_material) == kDifCsrngOk);
+
+  wait_for_csrng_cmd_ready(csrng);
+  CHECK(dif_csrng_generate(csrng, kExpectedOutputLen) == kDifCsrngOk);
+
+  dif_csrng_output_status_t output_status = {0};
+  while (!output_status.valid_data) {
+    CHECK(dif_csrng_get_output_status(csrng, &output_status) == kDifCsrngOk);
+  }
+
+  uint32_t output[16];
+  CHECK(dif_csrng_read_output(csrng, output, kExpectedOutputLen) ==
+        kDifCsrngOk);
+
+  // TODO(#5982): Enable CSRNG SW path without entropy source.
+  for (uint32_t i = 0; i < 16; ++i) {
+    LOG_INFO("[%d] got = 0x%x; expected = 0x%x", i, output[i],
+             kExpectedOutput[i]);
+  }
+}
+
 bool test_main() {
   const dif_csrng_params_t params = {
       .base_addr = mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR),
@@ -25,10 +107,7 @@
   };
   CHECK(dif_csrng_configure(&csrng, config) == kDifCsrngOk);
 
-  // TODO: Instantiate
-  // TODO: Generate
-  // TODO: Check for output ready
-  // TODO: Compare output
+  test_ctr_drbg_ctr0(&csrng);
 
   return true;
 }
diff --git a/sw/device/tests/dif/dif_csrng_unittest.cc b/sw/device/tests/dif/dif_csrng_unittest.cc
index 9e916a8..64a08dd 100644
--- a/sw/device/tests/dif/dif_csrng_unittest.cc
+++ b/sw/device/tests/dif/dif_csrng_unittest.cc
@@ -4,6 +4,9 @@
 
 #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/testing/mock_mmio.h"
@@ -132,5 +135,131 @@
   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, 0x00000101);
+  EXPECT_EQ(dif_csrng_instantiate(&csrng_, kDifCsrngEntropySrcToggleDisable,
+                                  &seed_material_),
+            kDifCsrngOk);
+
+  EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00000001);
+  EXPECT_EQ(dif_csrng_instantiate(&csrng_, kDifCsrngEntropySrcToggleEnable,
+                                  &seed_material_),
+            kDifCsrngOk);
+
+  seed_material_.seed_material[0] = 0x5a5a5a5a;
+  seed_material_.seed_material_len = 1;
+  EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00000011);
+  EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x5a5a5a5a);
+  EXPECT_EQ(dif_csrng_instantiate(&csrng_, kDifCsrngEntropySrcToggleEnable,
+                                  &seed_material_),
+            kDifCsrngOk);
+}
+
+TEST_F(CommandTest, InstantiateBadArgs) {
+  EXPECT_EQ(dif_csrng_instantiate(nullptr, kDifCsrngEntropySrcToggleDisable,
+                                  &seed_material_),
+            kDifCsrngBadArg);
+
+  // Failed overflow check.
+  seed_material_.seed_material_len = 16;
+  EXPECT_EQ(dif_csrng_instantiate(&csrng_, kDifCsrngEntropySrcToggleDisable,
+                                  &seed_material_),
+            kDifCsrngBadArg);
+}
+
+TEST_F(CommandTest, ReseedOk) {
+  EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00000002);
+  EXPECT_EQ(dif_csrng_reseed(&csrng_, &seed_material_), kDifCsrngOk);
+
+  seed_material_.seed_material[0] = 0x5a5a5a5a;
+  seed_material_.seed_material_len = 1;
+  EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00000012);
+  EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x5a5a5a5a);
+  EXPECT_EQ(dif_csrng_reseed(&csrng_, &seed_material_), kDifCsrngOk);
+}
+
+TEST_F(CommandTest, ReseedBadArgs) {
+  EXPECT_EQ(dif_csrng_reseed(nullptr, &seed_material_), kDifCsrngBadArg);
+
+  // Failed overflow check.
+  seed_material_.seed_material_len = 16;
+  EXPECT_EQ(dif_csrng_reseed(&csrng_, &seed_material_), kDifCsrngBadArg);
+}
+
+TEST_F(CommandTest, UpdateOk) {
+  EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00000004);
+  EXPECT_EQ(dif_csrng_update(&csrng_, &seed_material_), kDifCsrngOk);
+
+  seed_material_.seed_material[0] = 0x5a5a5a5a;
+  seed_material_.seed_material_len = 1;
+  EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00000014);
+  EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x5a5a5a5a);
+  EXPECT_EQ(dif_csrng_update(&csrng_, &seed_material_), kDifCsrngOk);
+}
+
+TEST_F(CommandTest, UpdateBadArgs) {
+  EXPECT_EQ(dif_csrng_update(nullptr, &seed_material_), kDifCsrngBadArg);
+}
+
+TEST_F(CommandTest, GenerateOk) {
+  // 512bits = 16 x 32bit = 4 x 128bit blocks
+  EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00004003);
+  EXPECT_EQ(dif_csrng_generate(&csrng_, /*len=*/16), kDifCsrngOk);
+
+  // 576bits = 18 x 32bit = 5 x 128bit blocks (rounded up)
+  EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00005003);
+  EXPECT_EQ(dif_csrng_generate(&csrng_, /*len=*/18), kDifCsrngOk);
+}
+
+TEST_F(CommandTest, GenerateBadArgs) {
+  EXPECT_EQ(dif_csrng_generate(nullptr, /*len=*/1), kDifCsrngBadArg);
+  EXPECT_EQ(dif_csrng_generate(&csrng_, /*len=*/0), kDifCsrngBadArg);
+}
+
+TEST_F(CommandTest, UninstantiateOk) {
+  EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00000005);
+  EXPECT_EQ(dif_csrng_uninstantiate(&csrng_), kDifCsrngOk);
+}
+
+class ReadOutputTest : public DifCsrngTest {};
+
+TEST_F(ReadOutputTest, ReadOk) {
+  constexpr std::array<uint32_t, 4> kExpected = {
+      0x00000000,
+      0x11111111,
+      0x22222222,
+      0x33333333,
+  };
+
+  for (const uint32_t val : kExpected) {
+    EXPECT_READ32(CSRNG_GENBITS_REG_OFFSET, val);
+  }
+
+  std::vector<uint32_t> got(kExpected.size());
+  EXPECT_EQ(dif_csrng_read_output(&csrng_, got.data(), got.size()),
+            kDifCsrngOk);
+  EXPECT_THAT(got, testing::ElementsAreArray(kExpected));
+}
+
+TEST_F(ReadOutputTest, ReadBadArgs) {
+  EXPECT_EQ(dif_csrng_read_output(&csrng_, nullptr, /*len=*/0),
+            kDifCsrngBadArg);
+
+  uint32_t data;
+  EXPECT_EQ(dif_csrng_read_output(nullptr, &data, /*len=*/1), kDifCsrngBadArg);
+}
+
 }  // namespace
 }  // namespace dif_entropy_unittest