Software APIs
edn_sca.c
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 
6 #include "sw/device/lib/base/status.h"
7 #include "sw/device/lib/dif/dif_rv_core_ibex.h"
10 #include "sw/device/lib/testing/rv_core_ibex_testutils.h"
11 #include "sw/device/lib/testing/test_framework/check.h"
12 #include "sw/device/lib/testing/test_framework/ujson_ottf.h"
13 #include "sw/device/lib/ujson/ujson.h"
14 #include "sw/device/tests/penetrationtests/firmware/lib/pentest_lib.h"
15 #include "sw/device/tests/penetrationtests/json/edn_sca_commands.h"
16 
17 #include "edn_regs.h" // Generated
19 #include "rv_core_ibex_regs.h" // Generated
20 
21 enum {
22  kEdnKatTimeout = (10 * 1000 * 1000),
23  kEdnKatMaxClen = 12,
24  kEdnKatOutputLen = 16,
25  kEdnKatWordsPerBlock = 4,
26  /**
27  * Max number of traces per batch.
28  */
29  kNumBatchOpsMax = 128,
30 };
31 
32 static dif_rv_core_ibex_t rv_core_ibex;
33 
34 /**
35  * Read randomness.
36  *
37  * The goal of this function is to allow the SCA setup to measure randomness
38  * transmitted from the 128-bit FIFO within the EDN to the Ibex RND_DATA
39  * register.
40  *
41  * To do so, this function first clears the 128-bit FIFO by reading it. When the
42  * FIFO is empty, the EDN sends a request to the CSRNG. When the FIFO again is
43  * full, set the SCA trigger and read the randomness from the FIFO to the
44  * RND_DATA register.
45  *
46  * @param ibex_rnd_data The array containing the randomness.
47  * @return OK or error.
48  */
49 static status_t read_rnd_data_reg(uint32_t ibex_rnd_data[4]) {
50  // Clear the CSRNG FIFO containing randomness before starting the test.
51  for (size_t it = 0; it < 4; it++) {
52  TRY(dif_rv_core_ibex_read_rnd_data(&rv_core_ibex, &ibex_rnd_data[0]));
53  }
54  memset(ibex_rnd_data, 0, 4 * sizeof(uint32_t));
55  // Wait until RND_DATA_VALID becomes true, i.e., randomness is available.
56  bool rnd_data_valid = rv_core_ibex_testutils_is_rnd_data_valid(&rv_core_ibex);
57  while (!rnd_data_valid) {
58  rnd_data_valid = rv_core_ibex_testutils_is_rnd_data_valid(&rv_core_ibex);
59  }
60 
61  // Read RND_DATA register. First access contains randomness that already was
62  // transmitted over the bus. Afterwards, data needs to be transmitted from the
63  // randomness FIFO into the RND_DATA register - this is what we want to
64  // measure.
65  pentest_set_trigger_high();
66  asm volatile(NOP30);
67  asm volatile("li t0, %0"
68  :
70  : "t0");
71  asm volatile("lw t1, %0(t0)"
72  :
73  : "i"(RV_CORE_IBEX_RND_DATA_REG_OFFSET)
74  : "t1");
75  asm volatile(NOP30);
76  asm volatile(NOP30);
77  pentest_set_trigger_low();
78  // Read RND_DATA which was transmitted from FIFO into RND_DATA register.
79  // Read it after the trigger window to not measure load into Ibex register
80  TRY(dif_rv_core_ibex_read_rnd_data(&rv_core_ibex, &ibex_rnd_data[3]));
81 
82  return OK_STATUS();
83 }
84 
85 status_t handle_edn_sca_bus_data_batch(ujson_t *uj) {
86  // Get number of iterations.
87  edn_sca_batch_t uj_data;
88  uint32_t max_iterations = 128;
89  TRY(ujson_deserialize_edn_sca_batch_t(uj, &uj_data));
90  CHECK(uj_data.num_iterations <= max_iterations);
91 
92  // Start num_iterations trigger windows.
93  uint32_t rand_data[max_iterations][4];
94  for (size_t it = 0; it < uj_data.num_iterations; it++) {
95  TRY(read_rnd_data_reg(rand_data[it]));
96  }
97 
98  // Send back num_iterations rand_data.
99  for (size_t it = 0; it < uj_data.num_iterations; it++) {
100  edn_sca_result_t uj_output;
101  memcpy(&uj_output.rnd_data, rand_data[it], 4 * sizeof(uint32_t));
102  RESP_OK(ujson_serialize_edn_sca_result_t, uj, &uj_output);
103  }
104 
105  return OK_STATUS();
106 }
107 
108 status_t handle_edn_sca_bus_data(ujson_t *uj) {
109  edn_sca_result_t uj_output;
110 
111  TRY(read_rnd_data_reg(uj_output.rnd_data));
112 
113  // Send data back to host.
114  RESP_OK(ujson_serialize_edn_sca_result_t, uj, &uj_output);
115  return OK_STATUS();
116 }
117 
118 status_t handle_edn_sca_init(ujson_t *uj) {
119  penetrationtest_cpuctrl_t uj_data;
120  TRY(ujson_deserialize_penetrationtest_cpuctrl_t(uj, &uj_data));
121 
122  pentest_select_trigger_type(kPentestTriggerTypeSw);
123  // As we are using the software defined trigger, the first argument of
124  // sca_init is not needed. kPentestTriggerSourceAes is selected as a
125  // placeholder.
126  pentest_init(kPentestTriggerSourceAes,
127  kPentestPeripheralIoDiv4 | kPentestPeripheralEntropy |
128  kPentestPeripheralCsrng | kPentestPeripheralEdn);
129 
130  // Disable the instruction cache and dummy instructions for SCA attacks.
131  penetrationtest_device_info_t uj_output;
132  TRY(pentest_configure_cpu(
133  uj_data.icache_disable, uj_data.dummy_instr_disable,
134  uj_data.enable_jittery_clock, uj_data.enable_sram_readback,
135  &uj_output.clock_jitter_locked, &uj_output.clock_jitter_en,
136  &uj_output.sram_main_readback_locked, &uj_output.sram_ret_readback_locked,
137  &uj_output.sram_main_readback_en, &uj_output.sram_ret_readback_en));
138 
139  // Configure Ibex to allow reading ERR_STATUS register.
140  TRY(dif_rv_core_ibex_init(
142  &rv_core_ibex));
143 
144  // Configure the entropy complex. Set the reseed interval to max to avoid
145  // reseed during the trigger window.
146  TRY(pentest_configure_entropy_source_max_reseed_interval());
147 
148  // Read device ID and return to host.
149  TRY(pentest_read_device_id(uj_output.device_id));
150  RESP_OK(ujson_serialize_penetrationtest_device_info_t, uj, &uj_output);
151 
152  return OK_STATUS();
153 }
154 
155 status_t handle_edn_sca(ujson_t *uj) {
156  edn_sca_subcommand_t cmd;
157  TRY(ujson_deserialize_edn_sca_subcommand_t(uj, &cmd));
158  switch (cmd) {
159  case kEdnScaSubcommandInit:
160  return handle_edn_sca_init(uj);
161  case kEdnScaSubcommandBusData:
162  return handle_edn_sca_bus_data(uj);
163  case kEdnScaSubcommandBusDataBatch:
164  return handle_edn_sca_bus_data_batch(uj);
165  default:
166  LOG_ERROR("Unrecognized EDN SCA subcommand: %d", cmd);
167  return INVALID_ARGUMENT();
168  }
169  return OK_STATUS();
170 }