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.
15 static const bitfield_field32_t kAppCmdFieldFlag0 = {.mask = 0xf, .index = 8};
16 static const bitfield_field32_t kAppCmdFieldCmdId = {.mask = 0xf, .index = 0};
17 static const bitfield_field32_t kAppCmdFieldCmdLen = {.mask = 0xf, .index = 4};
18 static const bitfield_field32_t kAppCmdFieldGlen = {.mask = 0x7ffff,
19  .index = 12};
20 
21 uint32_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);
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 
35 dif_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,
115  cmd.seed_material->seed_material[i]);
116  }
117  return kDifOk;
118 }