Software APIs
otbn_unittest.cc
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
5 #include "sw/device/silicon_creator/lib/drivers/otbn.h"
6 
7 #include <array>
8 #include <limits>
9 
10 #include "gtest/gtest.h"
11 #include "sw/device/lib/base/mock_abs_mmio.h"
12 #include "sw/device/silicon_creator/lib/base/mock_sec_mmio.h"
13 #include "sw/device/silicon_creator/lib/drivers/mock_rnd.h"
14 #include "sw/device/silicon_creator/testing/rom_test.h"
15 
17 #include "otbn_regs.h" // Generated.
18 
19 namespace otbn_unittest {
20 namespace {
21 using ::testing::ElementsAre;
22 using ::testing::Return;
23 
24 class OtbnTest : public rom_test::RomTest {
25  protected:
26  /**
27  * Sets expectations for running an OTBN command.
28  *
29  * @param cmd Command expected to be run.
30  * @param err_bits Error bits to be returned.
31  * @param status Status of OTBN to be returned after the command is done.
32  */
33  void ExpectCmdRun(sc_otbn_cmd_t cmd, uint32_t err_bits,
34  sc_otbn_status_t status) {
35  EXPECT_ABS_WRITE32(base_ + OTBN_INTR_STATE_REG_OFFSET,
36  {
37  {OTBN_INTR_COMMON_DONE_BIT, 1},
38  });
39  EXPECT_ABS_WRITE32(base_ + OTBN_CMD_REG_OFFSET, cmd);
40 
41  EXPECT_ABS_READ32(base_ + OTBN_INTR_STATE_REG_OFFSET, 0);
42  EXPECT_ABS_READ32(base_ + OTBN_INTR_STATE_REG_OFFSET,
43  {
44  {OTBN_INTR_COMMON_DONE_BIT, 1},
45  });
46  EXPECT_ABS_WRITE32(base_ + OTBN_INTR_STATE_REG_OFFSET,
47  {
48  {OTBN_INTR_COMMON_DONE_BIT, 1},
49  });
50 
51  EXPECT_ABS_READ32(base_ + OTBN_ERR_BITS_REG_OFFSET, err_bits);
52  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, status);
53 
54  if (err_bits == err_bits_ok_ && status == kScOtbnStatusIdle) {
55  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, status);
56  }
57  }
58 
59  uint32_t base_ = TOP_EARLGREY_OTBN_BASE_ADDR;
60  uint32_t err_bits_ok_ = 0;
61  rom_test::MockAbsMmio abs_mmio_;
62  rom_test::MockRnd rnd_;
63  rom_test::MockSecMmio sec_mmio_;
64 };
65 
66 class ExecuteTest : public OtbnTest {};
67 
68 TEST_F(ExecuteTest, ExecuteSuccess) {
69  // Test assumption.
70  static_assert(OTBN_IMEM_SIZE_BYTES >= 8, "OTBN IMEM size too small.");
71 
72  // Read twice for hardening.
73  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusIdle);
74  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusIdle);
75 
76  EXPECT_SEC_WRITE32(base_ + OTBN_CTRL_REG_OFFSET, 0x1);
77 
78  ExpectCmdRun(kScOtbnCmdExecute, err_bits_ok_, kScOtbnStatusIdle);
79 
80  EXPECT_EQ(sc_otbn_execute(), kErrorOk);
81 }
82 
83 TEST_F(ExecuteTest, ExecuteError) {
84  // Read twice for hardening.
85  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusIdle);
86  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusIdle);
87 
88  EXPECT_SEC_WRITE32(base_ + OTBN_CTRL_REG_OFFSET, 0x1);
89 
90  // Nonzero error bits.
91  ExpectCmdRun(kScOtbnCmdExecute, 1 << OTBN_ERR_BITS_FATAL_SOFTWARE_BIT,
92  kScOtbnStatusIdle);
93 
94  EXPECT_EQ(sc_otbn_execute(), kErrorOtbnExecutionFailed);
95 }
96 
97 TEST_F(ExecuteTest, ExecuteBusy) {
98  // Read twice for hardening.
99  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusIdle);
100  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusIdle);
101 
102  EXPECT_SEC_WRITE32(base_ + OTBN_CTRL_REG_OFFSET, 0x01);
103 
104  // Return a busy status after the `done` interrupt.
105  ExpectCmdRun(kScOtbnCmdExecute, err_bits_ok_, kScOtbnStatusBusyExecute);
106 
107  EXPECT_EQ(sc_otbn_execute(), kErrorOtbnExecutionFailed);
108 }
109 
110 TEST_F(ExecuteTest, ExecuteBlockUntilIdle) {
111  // Test assumption.
112  static_assert(OTBN_IMEM_SIZE_BYTES >= 8, "OTBN IMEM size too small.");
113 
114  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET,
115  kScOtbnStatusBusySecWipeDmem);
116  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET,
117  kScOtbnStatusBusySecWipeDmem);
118  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET,
119  kScOtbnStatusBusySecWipeDmem);
120 
121  // Read twice for hardening.
122  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusIdle);
123  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusIdle);
124 
125  EXPECT_SEC_WRITE32(base_ + OTBN_CTRL_REG_OFFSET, 0x1);
126 
127  ExpectCmdRun(kScOtbnCmdExecute, err_bits_ok_, kScOtbnStatusIdle);
128 
129  EXPECT_EQ(sc_otbn_execute(), kErrorOk);
130 }
131 
132 class IsBusyTest : public OtbnTest {};
133 
134 TEST_F(IsBusyTest, Success) {
135  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusBusyExecute);
136  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusBusyExecute);
137  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusBusyExecute);
138  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusBusyExecute);
139  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusIdle);
140  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusIdle);
141 
142  EXPECT_EQ(sc_otbn_busy_wait_for_done(), kErrorOk);
143 }
144 
145 class ImemSecWipeTest : public OtbnTest {};
146 
147 TEST_F(ImemSecWipeTest, Success) {
148  ExpectCmdRun(kScOtbnCmdSecWipeImem, err_bits_ok_, kScOtbnStatusIdle);
149 
150  EXPECT_EQ(sc_otbn_imem_sec_wipe(), kErrorOk);
151 }
152 
153 TEST_F(ImemSecWipeTest, Failure) {
154  ExpectCmdRun(kScOtbnCmdSecWipeImem, 1 << OTBN_ERR_BITS_FATAL_SOFTWARE_BIT,
155  kScOtbnStatusIdle);
156 
157  EXPECT_EQ(sc_otbn_imem_sec_wipe(), kErrorOtbnSecWipeImemFailed);
158 }
159 
160 class DmemSecWipeTest : public OtbnTest {};
161 
162 TEST_F(DmemSecWipeTest, Success) {
163  ExpectCmdRun(kScOtbnCmdSecWipeDmem, err_bits_ok_, kScOtbnStatusIdle);
164 
165  EXPECT_EQ(sc_otbn_dmem_sec_wipe(), kErrorOk);
166 }
167 
168 TEST_F(DmemSecWipeTest, Failure) {
169  ExpectCmdRun(kScOtbnCmdSecWipeDmem, 1 << OTBN_ERR_BITS_FATAL_SOFTWARE_BIT,
170  kScOtbnStatusIdle);
171 
172  EXPECT_EQ(sc_otbn_dmem_sec_wipe(), kErrorOtbnSecWipeDmemFailed);
173 }
174 
175 class DmemWriteTest : public OtbnTest {};
176 
177 TEST_F(DmemWriteTest, SuccessWithoutOffset) {
178  // Test assumption.
179  static_assert(OTBN_DMEM_SIZE_BYTES >= 8, "OTBN DMEM size too small.");
180 
181  std::array<uint32_t, 2> test_data = {0x12345678, 0xabcdef01};
182  sc_otbn_addr_t dest_addr = 0;
183 
184  EXPECT_CALL(rnd_, Uint32()).WillOnce(Return(0));
185  EXPECT_ABS_WRITE32(base_ + OTBN_DMEM_REG_OFFSET + dest_addr, test_data[0]);
186  EXPECT_ABS_WRITE32(base_ + OTBN_DMEM_REG_OFFSET + dest_addr + 4,
187  test_data[1]);
188 
189  EXPECT_EQ(sc_otbn_dmem_write(2, test_data.data(), dest_addr), kErrorOk);
190 }
191 
192 TEST_F(DmemWriteTest, SuccessWithOffset) {
193  // Test assumption.
194  static_assert(OTBN_DMEM_SIZE_BYTES >= 12, "OTBN DMEM size too small.");
195 
196  std::array<uint32_t, 2> test_data = {0x12345678, 0xabcdef01};
197  sc_otbn_addr_t dest_addr = 4;
198 
199  EXPECT_CALL(rnd_, Uint32()).WillOnce(Return(0));
200  EXPECT_ABS_WRITE32(base_ + OTBN_DMEM_REG_OFFSET + dest_addr, test_data[0]);
201  EXPECT_ABS_WRITE32(base_ + OTBN_DMEM_REG_OFFSET + dest_addr + 4,
202  test_data[1]);
203 
204  EXPECT_EQ(sc_otbn_dmem_write(2, test_data.data(), dest_addr), kErrorOk);
205 }
206 
207 TEST_F(DmemWriteTest, FailureOutOfRange) {
208  std::array<uint32_t, 2> test_data = {0x12345678, 0xabcdef01};
209  sc_otbn_addr_t dest_addr = OTBN_DMEM_SIZE_BYTES;
210 
211  EXPECT_EQ(sc_otbn_dmem_write(2, test_data.data(), dest_addr),
212  kErrorOtbnBadOffsetLen);
213 }
214 
215 TEST_F(DmemWriteTest, FailureOverflowNumWords) {
216  // Try to trigger an integer overflow with `num_words`.
217  size_t num_words =
218  (std::numeric_limits<size_t>::max() / sizeof(uint32_t)) + 1;
219  sc_otbn_addr_t dest_addr = 0;
220 
221  EXPECT_EQ(sc_otbn_dmem_write(num_words, NULL, dest_addr),
222  kErrorOtbnBadOffsetLen);
223 }
224 
225 TEST_F(DmemWriteTest, FailureOverflowOffset) {
226  // Try to trigger an integer overflow with `dest_addr`.
227  std::array<uint32_t, 2> test_data = {0x12345678, 0xabcdef01};
228  sc_otbn_addr_t dest_addr = std::numeric_limits<uint32_t>::max();
229 
230  EXPECT_EQ(sc_otbn_dmem_write(test_data.size(), test_data.data(), dest_addr),
231  kErrorOtbnBadOffsetLen);
232 }
233 
234 class DmemReadTest : public OtbnTest {};
235 
236 TEST_F(DmemReadTest, SuccessWithoutOffset) {
237  // Assumption in the test.
238  ASSERT_GE(OTBN_DMEM_SIZE_BYTES, 8);
239  static_assert(OTBN_DMEM_SIZE_BYTES >= 8, "OTBN DMEM size too small.");
240 
241  EXPECT_ABS_READ32(base_ + OTBN_DMEM_REG_OFFSET, 0x12345678);
242  EXPECT_ABS_READ32(base_ + OTBN_DMEM_REG_OFFSET + 4, 0xabcdef01);
243 
244  std::array<uint32_t, 2> test_data = {0};
245 
246  sc_otbn_addr_t src_addr = 0;
247  EXPECT_EQ(sc_otbn_dmem_read(2, src_addr, test_data.data()), kErrorOk);
248  EXPECT_THAT(test_data, ElementsAre(0x12345678, 0xabcdef01));
249 }
250 
251 TEST_F(DmemReadTest, SuccessWithOffset) {
252  // Assumption in the test.
253  static_assert(OTBN_DMEM_SIZE_BYTES >= 12, "OTBN DMEM size too small.");
254 
255  EXPECT_ABS_READ32(base_ + OTBN_DMEM_REG_OFFSET + 4, 0x12345678);
256  EXPECT_ABS_READ32(base_ + OTBN_DMEM_REG_OFFSET + 8, 0xabcdef01);
257 
258  std::array<uint32_t, 2> test_data = {0};
259 
260  sc_otbn_addr_t src_addr = 4;
261  EXPECT_EQ(sc_otbn_dmem_read(2, src_addr, test_data.data()), kErrorOk);
262  EXPECT_THAT(test_data, ElementsAre(0x12345678, 0xabcdef01));
263 }
264 
265 class OtbnAppTest : public OtbnTest {};
266 
267 TEST_F(OtbnAppTest, OtbnLoadAppSuccess) {
268  std::array<uint32_t, 2> imem_data = {0x01234567, 0x89abcdef};
269  std::array<uint32_t, 2> dmem_data = {0x456789ab, 0xcdef0123};
270  sc_otbn_addr_t dmem_data_offset = 0x12;
271  sc_otbn_app_t app = {
272  .imem_start = imem_data.data(),
273  .imem_end = imem_data.data() + imem_data.size(),
274  .dmem_data_start = dmem_data.data(),
275  .dmem_data_end = dmem_data.data() + imem_data.size(),
276  .dmem_data_start_addr = dmem_data_offset,
277  };
278 
279  // Test assumption.
280  static_assert(OTBN_DMEM_SIZE_BYTES >= sizeof(uint32_t) * dmem_data.size(),
281  "OTBN DMEM size too small");
282  static_assert(OTBN_IMEM_SIZE_BYTES >= sizeof(uint32_t) * imem_data.size(),
283  "OTBN IMEM size too small");
284 
285  // `sc_otbn_busy_wait_for_done` - begin with busy to ensure we wait until
286  // idle.
287  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusBusyExecute);
288  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET,
289  kScOtbnStatusBusySecWipeDmem);
290  // Read twice for hardening.
291  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusIdle);
292  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusIdle);
293  // `sc_otbn_dmem_sec_wipe`
294  ExpectCmdRun(kScOtbnCmdSecWipeDmem, err_bits_ok_, kScOtbnStatusIdle);
295  // `sc_otbn_imem_sec_wipe`
296  ExpectCmdRun(kScOtbnCmdSecWipeImem, err_bits_ok_, kScOtbnStatusIdle);
297  // `otbn_imem_write`
298  EXPECT_CALL(rnd_, Uint32()).WillOnce(Return(0));
299  EXPECT_ABS_WRITE32(base_ + OTBN_IMEM_REG_OFFSET, imem_data[0]);
300  EXPECT_ABS_WRITE32(base_ + OTBN_IMEM_REG_OFFSET + sizeof(uint32_t),
301  imem_data[1]);
302  // `sc_otbn_dmem_write`
303  EXPECT_CALL(rnd_, Uint32()).WillOnce(Return(0));
304  EXPECT_ABS_WRITE32(base_ + OTBN_DMEM_REG_OFFSET + dmem_data_offset,
305  dmem_data[0]);
306  EXPECT_ABS_WRITE32(
307  base_ + OTBN_DMEM_REG_OFFSET + dmem_data_offset + sizeof(uint32_t),
308  dmem_data[1]);
309 
310  EXPECT_EQ(sc_otbn_load_app(app), kErrorOk);
311 }
312 
313 TEST_F(OtbnAppTest, OtbnLoadInvalidAppEmptyImem) {
314  // Create an invalid app with an empty IMEM range.
315  std::array<uint32_t, 0> imem_data = {};
316  std::array<uint32_t, 2> dmem_data = {0x456789ab, 0xcdef0123};
317  sc_otbn_addr_t dmem_data_offset = 0x12;
318  sc_otbn_app_t app = {
319  .imem_start = imem_data.data(),
320  .imem_end = imem_data.data() + imem_data.size(),
321  .dmem_data_start = dmem_data.data(),
322  .dmem_data_end = dmem_data.data() + dmem_data.size(),
323  .dmem_data_start_addr = dmem_data_offset,
324  };
325 
326  // Test assumption.
327  static_assert(OTBN_DMEM_SIZE_BYTES >= sizeof(uint32_t) * dmem_data.size(),
328  "OTBN DMEM size too small");
329  static_assert(OTBN_IMEM_SIZE_BYTES >= sizeof(uint32_t) * imem_data.size(),
330  "OTBN IMEM size too small");
331 
332  EXPECT_EQ(sc_otbn_load_app(app), kErrorOtbnInvalidArgument);
333 }
334 
335 TEST_F(OtbnAppTest, OtbnLoadInvalidAppImemOutOfRange) {
336  // Create an invalid app with a too-large IMEM range.
337  std::array<uint32_t, (OTBN_IMEM_SIZE_BYTES / sizeof(uint32_t)) + 1>
338  imem_data = {0};
339  std::array<uint32_t, 2> dmem_data = {0x456789ab, 0xcdef0123};
340  sc_otbn_addr_t dmem_data_offset = 0x12;
341  sc_otbn_app_t app = {
342  .imem_start = imem_data.data(),
343  .imem_end = imem_data.data() + imem_data.size(),
344  .dmem_data_start = dmem_data.data(),
345  .dmem_data_end = dmem_data.data() + dmem_data.size(),
346  .dmem_data_start_addr = dmem_data_offset,
347  };
348 
349  // Read twice for hardening.
350  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusIdle);
351  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kScOtbnStatusIdle);
352  // `sc_otbn_dmem_sec_wipe`
353  ExpectCmdRun(kScOtbnCmdSecWipeDmem, err_bits_ok_, kScOtbnStatusIdle);
354  // `sc_otbn_imem_sec_wipe`
355  ExpectCmdRun(kScOtbnCmdSecWipeImem, err_bits_ok_, kScOtbnStatusIdle);
356 
357  EXPECT_EQ(sc_otbn_load_app(app), kErrorOtbnBadOffsetLen);
358 }
359 
360 class OtbnWriteTest : public OtbnTest {};
361 
362 TEST_F(OtbnWriteTest, Success) {
363  constexpr uint32_t kDestAddr = 6;
364  std::array<uint32_t, 2> test_data = {0x12345678, 0xabcdef01};
365 
366  // Test assumption.
367  static_assert(
368  OTBN_DMEM_SIZE_BYTES >= sizeof(uint32_t) * test_data.size() + kDestAddr,
369  "OTBN DMEM size too small.");
370 
371  EXPECT_CALL(rnd_, Uint32()).WillOnce(Return(0));
372  EXPECT_ABS_WRITE32(base_ + OTBN_DMEM_REG_OFFSET + kDestAddr, test_data[0]);
373  EXPECT_ABS_WRITE32(
374  base_ + OTBN_DMEM_REG_OFFSET + kDestAddr + sizeof(uint32_t),
375  test_data[1]);
376 
377  EXPECT_EQ(sc_otbn_dmem_write(2, test_data.data(), kDestAddr), kErrorOk);
378 }
379 
380 class OtbnReadTest : public OtbnTest {};
381 
382 TEST_F(OtbnReadTest, Success) {
383  constexpr uint32_t kSrcAddr = 6;
384  std::array<uint32_t, 2> test_data = {0};
385 
386  // Test assumption.
387  static_assert(
388  OTBN_DMEM_SIZE_BYTES >= sizeof(uint32_t) * test_data.size() + kSrcAddr,
389  "OTBN DMEM size too small.");
390 
391  EXPECT_ABS_READ32(base_ + OTBN_DMEM_REG_OFFSET + kSrcAddr, 0x12345678);
392  EXPECT_ABS_READ32(base_ + OTBN_DMEM_REG_OFFSET + kSrcAddr + sizeof(uint32_t),
393  0xabcdef01);
394 
395  EXPECT_EQ(sc_otbn_dmem_read(2, kSrcAddr, test_data.data()), kErrorOk);
396  EXPECT_THAT(test_data, ElementsAre(0x12345678, 0xabcdef01));
397 }
398 } // namespace
399 } // namespace otbn_unittest