Software APIs
dif_edn.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
8#include "sw/device/lib/base/multibits.h"
9#include "sw/device/lib/dif/dif_csrng_shared.h"
10
11#include "edn_regs.h" // Generated
12
13static dif_result_t check_locked(const dif_edn_t *edn) {
14 if (mmio_region_read32(edn->base_addr, EDN_REGWEN_REG_OFFSET) == 0) {
15 return kDifLocked;
16 }
17 return kDifOk;
18}
19
20dif_result_t dif_edn_configure(const dif_edn_t *edn) {
21 if (edn == NULL) {
22 return kDifBadArg;
23 }
24 DIF_RETURN_IF_ERROR(check_locked(edn));
25
26 uint32_t reg = mmio_region_read32(edn->base_addr, EDN_CTRL_REG_OFFSET);
27 reg = bitfield_field32_write(reg, EDN_CTRL_EDN_ENABLE_FIELD,
28 kMultiBitBool4True);
29 mmio_region_write32(edn->base_addr, EDN_CTRL_REG_OFFSET, reg);
30
31 return kDifOk;
32}
33
34dif_result_t dif_edn_lock(const dif_edn_t *edn) {
35 if (edn == NULL) {
36 return kDifBadArg;
37 }
38 mmio_region_write32(edn->base_addr, EDN_REGWEN_REG_OFFSET, 0);
39 return kDifOk;
40}
41
42dif_result_t dif_edn_is_locked(const dif_edn_t *edn, bool *is_locked) {
43 if (edn == NULL || is_locked == NULL) {
44 return kDifBadArg;
45 }
46 *is_locked = check_locked(edn) != kDifOk;
47 return kDifOk;
48}
49
50dif_result_t dif_edn_set_boot_mode(const dif_edn_t *edn) {
51 if (edn == NULL) {
52 return kDifBadArg;
53 }
54 DIF_RETURN_IF_ERROR(check_locked(edn));
55
56 uint32_t reg = mmio_region_read32(edn->base_addr, EDN_CTRL_REG_OFFSET);
57 reg = bitfield_field32_write(reg, EDN_CTRL_BOOT_REQ_MODE_FIELD,
58 kMultiBitBool4True);
59 mmio_region_write32(edn->base_addr, EDN_CTRL_REG_OFFSET, reg);
60
61 return kDifOk;
62}
63
64dif_result_t dif_edn_set_auto_mode(const dif_edn_t *edn,
65 dif_edn_auto_params_t config) {
66 if (edn == NULL) {
67 return kDifBadArg;
68 }
69 DIF_RETURN_IF_ERROR(check_locked(edn));
70
71 // Check that EDN is disabled. If it is not disabled (e.g., through a call
72 // to `dif_edn_stop()`), we are not ready to change mode.
73 uint32_t ctrl_reg = mmio_region_read32(edn->base_addr, EDN_CTRL_REG_OFFSET);
74 const uint32_t edn_en =
75 bitfield_field32_read(ctrl_reg, EDN_CTRL_EDN_ENABLE_FIELD);
76 if (dif_multi_bit_bool_to_toggle(edn_en) != kDifToggleDisabled) {
77 return kDifError;
78 }
79
80 // Ensure neither automatic nor boot request mode is set.
81 ctrl_reg = bitfield_field32_write(ctrl_reg, EDN_CTRL_AUTO_REQ_MODE_FIELD,
82 kMultiBitBool4False);
83 ctrl_reg = bitfield_field32_write(ctrl_reg, EDN_CTRL_BOOT_REQ_MODE_FIELD,
84 kMultiBitBool4False);
85 mmio_region_write32(edn->base_addr, EDN_CTRL_REG_OFFSET, ctrl_reg);
86
87 // Clear the reseed command FIFO and the generate command FIFO.
88 ctrl_reg = bitfield_field32_write(ctrl_reg, EDN_CTRL_CMD_FIFO_RST_FIELD,
89 kMultiBitBool4True);
90 mmio_region_write32(edn->base_addr, EDN_CTRL_REG_OFFSET, ctrl_reg);
91
92 // Restore command FIFOs to normal operation mode. This is a prerequisite
93 // before any further commands can be issued to these FIFOs.
94 ctrl_reg = bitfield_field32_write(ctrl_reg, EDN_CTRL_CMD_FIFO_RST_FIELD,
95 kMultiBitBool4False);
96 mmio_region_write32(edn->base_addr, EDN_CTRL_REG_OFFSET, ctrl_reg);
97
98 // Fill the reseed command FIFO.
99 mmio_region_write32(edn->base_addr, EDN_RESEED_CMD_REG_OFFSET,
100 config.reseed_cmd.cmd);
101 for (size_t i = 0; i < config.reseed_cmd.seed_material.len; ++i) {
102 mmio_region_write32(edn->base_addr, EDN_RESEED_CMD_REG_OFFSET,
103 config.reseed_cmd.seed_material.data[i]);
104 }
105
106 // Fill the generate command FIFO.
107 mmio_region_write32(edn->base_addr, EDN_GENERATE_CMD_REG_OFFSET,
108 config.generate_cmd.cmd);
109 for (size_t i = 0; i < config.generate_cmd.seed_material.len; ++i) {
110 mmio_region_write32(edn->base_addr, EDN_GENERATE_CMD_REG_OFFSET,
112 }
113
114 // Set the maximum number of requests between reseeds.
115 mmio_region_write32(edn->base_addr,
116 EDN_MAX_NUM_REQS_BETWEEN_RESEEDS_REG_OFFSET,
117 config.reseed_interval);
118
119 // Re-enable EDN in automatic request mode.
120 ctrl_reg = bitfield_field32_write(ctrl_reg, EDN_CTRL_EDN_ENABLE_FIELD,
121 kMultiBitBool4True);
122 ctrl_reg = bitfield_field32_write(ctrl_reg, EDN_CTRL_AUTO_REQ_MODE_FIELD,
123 kMultiBitBool4True);
124 mmio_region_write32(edn->base_addr, EDN_CTRL_REG_OFFSET, ctrl_reg);
125
126 // Wait until EDN is ready to accept commands.
127 bool ready = false;
128 while (!ready) {
129 DIF_RETURN_IF_ERROR(dif_edn_get_status(edn, kDifEdnStatusReady, &ready));
130 }
131
132 // Command CSRNG Instantiate. As soon as CSRNG acknowledges this command,
133 // EDN will start automatically sending reseed and generate commands.
134 const dif_edn_entropy_src_toggle_t entropy_src_enable =
135 ((config.instantiate_cmd.cmd & 0xF00) >> 8) == kMultiBitBool4True
138 DIF_RETURN_IF_ERROR(dif_edn_instantiate(
139 edn, entropy_src_enable, &config.instantiate_cmd.seed_material));
140
141 // Wait until CSRNG acknowledges command.
142 ready = false;
143 while (!ready) {
144 DIF_RETURN_IF_ERROR(dif_edn_get_status(edn, kDifEdnStatusCsrngAck, &ready));
145 }
146
147 // Read request acknowledge error and return accordingly.
148 bool ack_err;
150 dif_edn_get_status(edn, kDifEdnStatusCsrngStatus, &ack_err));
151 return ack_err ? kDifError : kDifOk;
152}
153
154dif_result_t dif_edn_get_status(const dif_edn_t *edn, dif_edn_status_t flag,
155 bool *set) {
156 if (edn == NULL || set == NULL) {
157 return kDifBadArg;
158 }
159
160 uint32_t reg = mmio_region_read32(edn->base_addr, EDN_SW_CMD_STS_REG_OFFSET);
161 uint32_t field_val;
162 switch (flag) {
164 *set = bitfield_bit32_read(reg, EDN_SW_CMD_STS_CMD_REG_RDY_BIT);
165 break;
167 *set = bitfield_bit32_read(reg, EDN_SW_CMD_STS_CMD_RDY_BIT);
168 break;
170 field_val = bitfield_field32_read(reg, EDN_SW_CMD_STS_CMD_STS_FIELD);
171 *set = field_val ? true : false;
172 break;
174 *set = bitfield_bit32_read(reg, EDN_SW_CMD_STS_CMD_ACK_BIT);
175 break;
176 default:
177 return kDifBadArg;
178 }
179
180 return kDifOk;
181}
182
183dif_result_t dif_edn_get_errors(const dif_edn_t *edn, uint32_t *unhealthy_fifos,
184 uint32_t *errors) {
185 if (edn == NULL || unhealthy_fifos == NULL || errors == NULL) {
186 return kDifBadArg;
187 }
188 *unhealthy_fifos = 0;
189 *errors = 0;
190
191 uint32_t reg = mmio_region_read32(edn->base_addr, EDN_ERR_CODE_REG_OFFSET);
192 *unhealthy_fifos =
193 bitfield_bit32_copy(*unhealthy_fifos, kDifEdnFifoReseedCmd, reg,
194 EDN_ERR_CODE_SFIFO_RESCMD_ERR_BIT);
195 *unhealthy_fifos =
196 bitfield_bit32_copy(*unhealthy_fifos, kDifEdnFifoGenerateCmd, reg,
197 EDN_ERR_CODE_SFIFO_GENCMD_ERR_BIT);
198
199 *errors = bitfield_bit32_copy(*errors, kDifEdnErrorAckSm, reg,
200 EDN_ERR_CODE_EDN_ACK_SM_ERR_BIT);
201 *errors = bitfield_bit32_copy(*errors, kDifEdnErrorMainSm, reg,
202 EDN_ERR_CODE_EDN_MAIN_SM_ERR_BIT);
203 *errors = bitfield_bit32_copy(*errors, kDifEdnErrorCounterFault, reg,
204 EDN_ERR_CODE_EDN_CNTR_ERR_BIT);
205 *errors = bitfield_bit32_copy(*errors, kDifEdnErrorFifoWrite, reg,
206 EDN_ERR_CODE_FIFO_WRITE_ERR_BIT);
207 *errors = bitfield_bit32_copy(*errors, kDifEdnErrorFifoRead, reg,
208 EDN_ERR_CODE_FIFO_READ_ERR_BIT);
209 *errors = bitfield_bit32_copy(*errors, kDifEdnErrorFifoFullAndEmpty, reg,
210 EDN_ERR_CODE_FIFO_STATE_ERR_BIT);
211
212 return kDifOk;
213}
214
215dif_result_t dif_edn_get_cmd_unhealthy_fifo_force(const dif_edn_t *edn,
216 dif_edn_fifo_t fifo) {
217 if (edn == NULL) {
218 return kDifBadArg;
219 }
220
221 uint32_t fifo_bit;
222 switch (fifo) {
223 case kDifEdnFifoReseedCmd:
224 fifo_bit = EDN_ERR_CODE_SFIFO_RESCMD_ERR_BIT;
225 break;
226 case kDifEdnFifoGenerateCmd:
227 fifo_bit = EDN_ERR_CODE_SFIFO_GENCMD_ERR_BIT;
228 break;
229 default:
230 return kDifBadArg;
231 }
232
233 DIF_RETURN_IF_ERROR(check_locked(edn));
234 mmio_region_write32(edn->base_addr, EDN_ERR_CODE_TEST_REG_OFFSET, fifo_bit);
235 return kDifOk;
236}
237
238dif_result_t dif_edn_get_cmd_error_force(const dif_edn_t *edn,
239 dif_edn_error_t error) {
240 if (edn == NULL) {
241 return kDifBadArg;
242 }
243
244 uint32_t error_bit;
245 switch (error) {
247 error_bit = EDN_ERR_CODE_EDN_ACK_SM_ERR_BIT;
248 break;
250 error_bit = EDN_ERR_CODE_EDN_MAIN_SM_ERR_BIT;
251 break;
253 error_bit = EDN_ERR_CODE_EDN_CNTR_ERR_BIT;
254 break;
256 error_bit = EDN_ERR_CODE_FIFO_WRITE_ERR_BIT;
257 break;
259 error_bit = EDN_ERR_CODE_FIFO_READ_ERR_BIT;
260 break;
262 error_bit = EDN_ERR_CODE_FIFO_STATE_ERR_BIT;
263 break;
264 default:
265 return kDifBadArg;
266 }
267
268 DIF_RETURN_IF_ERROR(check_locked(edn));
269 mmio_region_write32(edn->base_addr, EDN_ERR_CODE_TEST_REG_OFFSET, error_bit);
270 return kDifOk;
271}
272
273dif_result_t dif_edn_get_main_state_machine(const dif_edn_t *edn,
274 uint32_t *state) {
275 if (edn == NULL || state == NULL) {
276 return kDifBadArg;
277 }
278
279 *state = mmio_region_read32(edn->base_addr, EDN_MAIN_SM_STATE_REG_OFFSET);
280 return kDifOk;
281}
282
283dif_result_t dif_edn_instantiate(
284 const dif_edn_t *edn, dif_edn_entropy_src_toggle_t entropy_src_enable,
285 const dif_edn_seed_material_t *seed_material) {
286 if (edn == NULL) {
287 return kDifBadArg;
288 }
289 return csrng_send_app_cmd(
290 edn->base_addr, kCsrngAppCmdTypeEdnSw,
291 (csrng_app_cmd_t){
292 .id = kCsrngAppCmdInstantiate,
293 .entropy_src_enable =
294 (dif_csrng_entropy_src_toggle_t)entropy_src_enable,
295 .seed_material = (const dif_csrng_seed_material_t *)seed_material,
296 });
297}
298
299dif_result_t dif_edn_reseed(const dif_edn_t *edn,
300 const dif_edn_seed_material_t *seed_material) {
301 if (edn == NULL || seed_material == NULL) {
302 return kDifBadArg;
303 }
304 dif_csrng_seed_material_t seed_material2;
305 memcpy(&seed_material2, seed_material, sizeof(seed_material2));
306 return csrng_send_app_cmd(edn->base_addr, kCsrngAppCmdTypeEdnSw,
307 (csrng_app_cmd_t){
308 .id = kCsrngAppCmdReseed,
309 .seed_material = &seed_material2,
310 });
311}
312
313dif_result_t dif_edn_update(const dif_edn_t *edn,
314 const dif_edn_seed_material_t *seed_material) {
315 if (edn == NULL || seed_material == NULL) {
316 return kDifBadArg;
317 }
318 dif_csrng_seed_material_t seed_material2;
319 memcpy(&seed_material2, seed_material, sizeof(seed_material2));
320 return csrng_send_app_cmd(edn->base_addr, kCsrngAppCmdTypeEdnSw,
321 (csrng_app_cmd_t){
322 .id = kCsrngAppCmdUpdate,
323 .seed_material = &seed_material2,
324 });
325}
326
327dif_result_t dif_edn_generate_start(const dif_edn_t *edn, size_t len) {
328 if (edn == NULL || len == 0) {
329 return kDifBadArg;
330 }
331
332 // Round up the number of 128bit blocks. Aligning with respect to uint32_t.
333 // TODO(#6112): Consider using a canonical reference for alignment operations.
334 const uint32_t num_128bit_blocks = (len + 3) / 4;
335 return csrng_send_app_cmd(edn->base_addr, kCsrngAppCmdTypeEdnSw,
336 (csrng_app_cmd_t){
337 .id = kCsrngAppCmdGenerate,
338 .generate_len = num_128bit_blocks,
339 });
340}
341
342dif_result_t dif_edn_uninstantiate(const dif_edn_t *edn) {
343 if (edn == NULL) {
344 return kDifBadArg;
345 }
346 return csrng_send_app_cmd(edn->base_addr, kCsrngAppCmdTypeEdnSw,
347 (csrng_app_cmd_t){
348 .id = kCsrngAppCmdUninstantiate,
349 });
350}
351
352dif_result_t dif_edn_stop(const dif_edn_t *edn) {
353 if (edn == NULL) {
354 return kDifBadArg;
355 }
356 DIF_RETURN_IF_ERROR(check_locked(edn));
357
358 // Fifo clear is only honored if edn is enabled.
359 uint32_t reg = mmio_region_read32(edn->base_addr, EDN_CTRL_REG_OFFSET);
360 reg = bitfield_field32_write(reg, EDN_CTRL_CMD_FIFO_RST_FIELD,
361 kMultiBitBool4True);
362 mmio_region_write32(edn->base_addr, EDN_CTRL_REG_OFFSET, reg);
363
364 // Disable edn and restore Fifo clear at the same time so that no rogue
365 // command can get in after the clear above.
366 mmio_region_write32(edn->base_addr, EDN_CTRL_REG_OFFSET, EDN_CTRL_REG_RESVAL);
367
368 return kDifOk;
369}
370
371dif_result_t dif_edn_get_recoverable_alerts(const dif_edn_t *edn,
372 uint32_t *alerts) {
373 if (edn == NULL || alerts == NULL) {
374 return kDifBadArg;
375 }
376
377 *alerts = 0;
378 uint32_t reg =
379 mmio_region_read32(edn->base_addr, EDN_RECOV_ALERT_STS_REG_OFFSET);
380 *alerts = bitfield_bit32_copy(*alerts, kDifEdnRecoverableAlertBadEnable, reg,
381 EDN_RECOV_ALERT_STS_EDN_ENABLE_FIELD_ALERT_BIT);
382 *alerts =
383 bitfield_bit32_copy(*alerts, kDifEdnRecoverableAlertBadBootReqMode, reg,
384 EDN_RECOV_ALERT_STS_BOOT_REQ_MODE_FIELD_ALERT_BIT);
385 *alerts =
386 bitfield_bit32_copy(*alerts, kDifEdnRecoverableAlertBadAutoReqMode, reg,
387 EDN_RECOV_ALERT_STS_AUTO_REQ_MODE_FIELD_ALERT_BIT);
388 *alerts =
389 bitfield_bit32_copy(*alerts, kDifEdnRecoverableAlertBadFifoClear, reg,
390 EDN_RECOV_ALERT_STS_CMD_FIFO_RST_FIELD_ALERT_BIT);
391 *alerts = bitfield_bit32_copy(*alerts, kDifEdnRecoverableAlertRepeatedGenBits,
392 reg, EDN_RECOV_ALERT_STS_EDN_BUS_CMP_ALERT_BIT);
393 return kDifOk;
394}
395
396dif_result_t dif_edn_clear_recoverable_alerts(const dif_edn_t *edn) {
397 if (edn == NULL) {
398 return kDifBadArg;
399 }
400 mmio_region_write32(edn->base_addr, EDN_RECOV_ALERT_STS_REG_OFFSET, 0);
401 return kDifOk;
402}