Software APIs
dif_csrng_shared.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
5#include "sw/device/lib/dif/dif_csrng_shared.h"
6
7#include "sw/device/lib/base/multibits.h"
8
9#include "csrng_regs.h" // Generated
10#include "edn_regs.h" // Generated
11
12// The application command header is not specified as a register in the
13// hardware specification, so the fields are mapped here by hand. The
14// command register also accepts arbitrary 32bit data.
15static const bitfield_field32_t kAppCmdFieldFlag0 = {.mask = 0xf, .index = 8};
16static const bitfield_field32_t kAppCmdFieldCmdId = {.mask = 0xf, .index = 0};
17static const bitfield_field32_t kAppCmdFieldCmdLen = {.mask = 0xf, .index = 4};
18static const bitfield_field32_t kAppCmdFieldGlen = {.mask = 0x7ffff,
19 .index = 12};
20
21uint32_t csrng_cmd_header_build(
22 csrng_app_cmd_id_t id, dif_csrng_entropy_src_toggle_t entropy_src_enable,
23 uint32_t cmd_len, uint32_t generate_len) {
24 uint32_t reg = bitfield_field32_write(0, kAppCmdFieldCmdId, id);
25 reg = bitfield_field32_write(reg, kAppCmdFieldCmdLen, cmd_len);
26 reg = bitfield_field32_write(
27 reg, kAppCmdFieldFlag0,
28 (entropy_src_enable == kDifCsrngEntropySrcToggleDisable
29 ? kMultiBitBool4True
30 : kMultiBitBool4False));
31 reg = bitfield_field32_write(reg, kAppCmdFieldGlen, generate_len);
32 return reg;
33}
34
35dif_result_t csrng_send_app_cmd(mmio_region_t base_addr,
36 csrng_app_cmd_type_t cmd_type,
37 csrng_app_cmd_t cmd) {
38 ptrdiff_t cmd_reg_offset;
39 ptrdiff_t sts_reg_offset;
40 uint32_t rdy_bit_offset;
41 uint32_t reg_rdy_bit_offset;
42 uint32_t reg;
43 bool ready;
44
45 switch (cmd_type) {
46 case kCsrngAppCmdTypeCsrng:
47 cmd_reg_offset = CSRNG_CMD_REQ_REG_OFFSET;
48 sts_reg_offset = CSRNG_SW_CMD_STS_REG_OFFSET;
49 rdy_bit_offset = CSRNG_SW_CMD_STS_CMD_RDY_BIT;
50 reg_rdy_bit_offset = CSRNG_SW_CMD_STS_CMD_RDY_BIT;
51 break;
52 case kCsrngAppCmdTypeEdnSw:
53 cmd_reg_offset = EDN_SW_CMD_REQ_REG_OFFSET;
54 sts_reg_offset = EDN_SW_CMD_STS_REG_OFFSET;
55 rdy_bit_offset = EDN_SW_CMD_STS_CMD_RDY_BIT;
56 reg_rdy_bit_offset = EDN_SW_CMD_STS_CMD_REG_RDY_BIT;
57 break;
58 case kCsrngAppCmdTypeEdnGen:
59 cmd_reg_offset = EDN_GENERATE_CMD_REG_OFFSET;
60 break;
61 case kCsrngAppCmdTypeEdnRes:
62 cmd_reg_offset = EDN_RESEED_CMD_REG_OFFSET;
63 break;
64 default:
65 return kDifBadArg;
66 }
67
68 // Ensure the `seed_material` array is word-aligned, so it can be loaded to a
69 // CPU register with natively aligned loads.
70 if (cmd.seed_material != NULL &&
71 misalignment32_of((uintptr_t)cmd.seed_material->seed_material) != 0) {
72 return kDifBadArg;
73 }
74
75 uint32_t cmd_len =
76 cmd.seed_material == NULL ? 0 : cmd.seed_material->seed_material_len;
77 if (cmd_len & ~kAppCmdFieldCmdLen.mask) {
78 return kDifBadArg;
79 }
80
81 enum {
82 // This is to maintain full compliance with NIST SP 800-90A, which requires
83 // the max generate output to be constrained to gen < 2 ^ 12 bits or 0x800
84 // 128-bit blocks.
85 kMaxGenerateSizeIn128BitBlocks = 0x800,
86 };
87 if (cmd.generate_len > kMaxGenerateSizeIn128BitBlocks) {
88 return kDifOutOfRange;
89 }
90
91 if ((cmd_type == kCsrngAppCmdTypeCsrng) ||
92 (cmd_type == kCsrngAppCmdTypeEdnSw)) {
93 // Wait for the status register to be ready to accept the next command.
94 do {
95 reg = mmio_region_read32(base_addr, sts_reg_offset);
96 ready = bitfield_bit32_read(reg, rdy_bit_offset);
97 } while (!ready);
98 }
99
100 mmio_region_write32(base_addr, cmd_reg_offset,
101 csrng_cmd_header_build(cmd.id, cmd.entropy_src_enable,
102 cmd_len, cmd.generate_len));
103 for (size_t i = 0; i < cmd_len; ++i) {
104 // Before writing each word of additional data, the command ready or command
105 // reg ready bit needs to be polled if the command is issued to CSRNG or the
106 // SW register of EDN, respectively.
107 if (cmd_type == kCsrngAppCmdTypeCsrng ||
108 cmd_type == kCsrngAppCmdTypeEdnSw) {
109 do {
110 reg = mmio_region_read32(base_addr, sts_reg_offset);
111 ready = bitfield_bit32_read(reg, reg_rdy_bit_offset);
112 } while (!ready);
113 }
114 mmio_region_write32(base_addr, cmd_reg_offset,
116 }
117 return kDifOk;
118}