[sw/silicon_creator] Randomize the start of OTBN write loops

Signed-off-by: Alphan Ulusoy <alphan@google.com>
diff --git a/sw/device/silicon_creator/lib/drivers/BUILD b/sw/device/silicon_creator/lib/drivers/BUILD
index 9466c50..cb0ea9c 100644
--- a/sw/device/silicon_creator/lib/drivers/BUILD
+++ b/sw/device/silicon_creator/lib/drivers/BUILD
@@ -282,6 +282,7 @@
         "//sw/device/lib/base:abs_mmio",
         "//sw/device/lib/base:bitfield",
         "//sw/device/silicon_creator/lib:error",
+        "//sw/device/silicon_creator/lib/drivers:rnd",
     ],
 )
 
diff --git a/sw/device/silicon_creator/lib/drivers/meson.build b/sw/device/silicon_creator/lib/drivers/meson.build
index bd3bc39..791aefe 100644
--- a/sw/device/silicon_creator/lib/drivers/meson.build
+++ b/sw/device/silicon_creator/lib/drivers/meson.build
@@ -499,6 +499,7 @@
     ],
     dependencies: [
       sw_lib_abs_mmio,
+      sw_silicon_creator_lib_driver_rnd,
     ],
   ),
 )
diff --git a/sw/device/silicon_creator/lib/drivers/otbn.c b/sw/device/silicon_creator/lib/drivers/otbn.c
index d944c9c..90a578d 100644
--- a/sw/device/silicon_creator/lib/drivers/otbn.c
+++ b/sw/device/silicon_creator/lib/drivers/otbn.c
@@ -10,6 +10,7 @@
 
 #include "sw/device/lib/base/abs_mmio.h"
 #include "sw/device/lib/base/bitfield.h"
+#include "sw/device/silicon_creator/lib/drivers/rnd.h"
 #include "sw/device/silicon_creator/lib/error.h"
 
 #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
@@ -57,6 +58,30 @@
   return kErrorOk;
 }
 
+/**
+ * Helper function for writing to OTBN's DMEM or IMEM.
+ *
+ * @param dest_addr Destination address.
+ * @param src Source buffer.
+ * @param num_words Number of words to copy.
+ */
+static void otbn_write(uint32_t dest_addr, const uint32_t *src,
+                       size_t num_words) {
+  // Start from a random index less than `num_words`.
+  size_t i = ((uint64_t)rnd_uint32() * (uint64_t)num_words) >> 32;
+  enum { kStep = 1 };
+  size_t iter_cnt = 0;
+  for (; launder32(iter_cnt) < num_words; ++iter_cnt) {
+    abs_mmio_write32(dest_addr + i * sizeof(uint32_t), src[i]);
+    i += kStep;
+    if (launder32(i) >= num_words) {
+      i -= num_words;
+    }
+    HARDENED_CHECK_LT(i, num_words);
+  }
+  HARDENED_CHECK_EQ(iter_cnt, num_words);
+}
+
 void otbn_execute(void) {
   abs_mmio_write32(kBase + OTBN_CMD_REG_OFFSET, kOtbnCmdExecute);
 }
@@ -74,15 +99,7 @@
                             size_t num_words) {
   RETURN_IF_ERROR(
       check_offset_len(offset_bytes, num_words, kOtbnIMemSizeBytes));
-
-  size_t i = 0;
-  for (; launder32(i) < num_words; ++i) {
-    abs_mmio_write32(
-        kBase + OTBN_IMEM_REG_OFFSET + offset_bytes + i * sizeof(uint32_t),
-        src[i]);
-  }
-  HARDENED_CHECK_EQ(i, num_words);
-
+  otbn_write(kBase + OTBN_IMEM_REG_OFFSET + offset_bytes, src, num_words);
   return kErrorOk;
 }
 
@@ -90,15 +107,7 @@
                             size_t num_words) {
   RETURN_IF_ERROR(
       check_offset_len(offset_bytes, num_words, kOtbnDMemSizeBytes));
-
-  size_t i = 0;
-  for (; launder32(i) < num_words; ++i) {
-    abs_mmio_write32(
-        kBase + OTBN_DMEM_REG_OFFSET + offset_bytes + i * sizeof(uint32_t),
-        src[i]);
-  }
-  HARDENED_CHECK_EQ(i, num_words);
-
+  otbn_write(kBase + OTBN_DMEM_REG_OFFSET + offset_bytes, src, num_words);
   return kErrorOk;
 }
 
diff --git a/sw/device/silicon_creator/lib/drivers/otbn_unittest.cc b/sw/device/silicon_creator/lib/drivers/otbn_unittest.cc
index 60af1a0..25ce156 100644
--- a/sw/device/silicon_creator/lib/drivers/otbn_unittest.cc
+++ b/sw/device/silicon_creator/lib/drivers/otbn_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "gtest/gtest.h"
 #include "sw/device/lib/base/testing/mock_abs_mmio.h"
+#include "sw/device/silicon_creator/lib/drivers/mock_rnd.h"
 #include "sw/device/silicon_creator/testing/mask_rom_test.h"
 
 #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
@@ -16,11 +17,13 @@
 namespace otbn_unittest {
 namespace {
 using ::testing::ElementsAre;
+using ::testing::Return;
 
 class OtbnTest : public mask_rom_test::MaskRomTest {
  protected:
   uint32_t base_ = TOP_EARLGREY_OTBN_BASE_ADDR;
   mask_rom_test::MockAbsMmio mmio_;
+  mask_rom_test::MockRnd rnd_;
 };
 
 class StartTest : public OtbnTest {};
@@ -76,6 +79,7 @@
 
   std::array<uint32_t, 2> test_data = {0x12345678, 0xabcdef01};
 
+  EXPECT_CALL(rnd_, Uint32()).WillOnce(Return(0));
   EXPECT_ABS_WRITE32(base_ + OTBN_IMEM_REG_OFFSET, test_data[0]);
   EXPECT_ABS_WRITE32(base_ + OTBN_IMEM_REG_OFFSET + 4, test_data[1]);
 
@@ -88,6 +92,7 @@
 
   std::array<uint32_t, 2> test_data = {0x12345678, 0xabcdef01};
 
+  EXPECT_CALL(rnd_, Uint32()).WillOnce(Return(0));
   EXPECT_ABS_WRITE32(base_ + OTBN_IMEM_REG_OFFSET + 4, test_data[0]);
   EXPECT_ABS_WRITE32(base_ + OTBN_IMEM_REG_OFFSET + 8, test_data[1]);
 
@@ -102,6 +107,7 @@
 
   std::array<uint32_t, 2> test_data = {0x12345678, 0xabcdef01};
 
+  EXPECT_CALL(rnd_, Uint32()).WillOnce(Return(0));
   EXPECT_ABS_WRITE32(base_ + OTBN_DMEM_REG_OFFSET, test_data[0]);
   EXPECT_ABS_WRITE32(base_ + OTBN_DMEM_REG_OFFSET + 4, test_data[1]);
 
@@ -114,6 +120,7 @@
 
   std::array<uint32_t, 2> test_data = {0x12345678, 0xabcdef01};
 
+  EXPECT_CALL(rnd_, Uint32()).WillOnce(Return(0));
   EXPECT_ABS_WRITE32(base_ + OTBN_DMEM_REG_OFFSET + 4, test_data[0]);
   EXPECT_ABS_WRITE32(base_ + OTBN_DMEM_REG_OFFSET + 8, test_data[1]);