|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | #include "sw/device/lib/crypto/drivers/entropy.h" | 
|  |  | 
|  | #include "sw/device/lib/base/abs_mmio.h" | 
|  | #include "sw/device/lib/base/bitfield.h" | 
|  | #include "sw/device/lib/base/memory.h" | 
|  |  | 
|  | #include "csrng_regs.h"  // Generated | 
|  | #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" | 
|  |  | 
|  | enum { | 
|  | kBaseCsrng = TOP_EARLGREY_CSRNG_BASE_ADDR, | 
|  |  | 
|  | /** | 
|  | * CSRNG genbits buffer size in uint32_t words. | 
|  | */ | 
|  | kEntropyCsrngBitsBufferNumWords = 4, | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Supported CSRNG application commands. | 
|  | * See https://docs.opentitan.org/hw/ip/csrng/doc/#command-header for | 
|  | * details. | 
|  | */ | 
|  | // TODO(#14542): Harden csrng/edn command fields. | 
|  | typedef enum entropy_csrng_op { | 
|  | kEntropyDrbgOpInstantiate = 1, | 
|  | kEntropyDrbgOpReseed = 2, | 
|  | kEntropyDrbgOpGenerate = 3, | 
|  | kEntropyDrbgOpUpdate = 4, | 
|  | kEntropyDrbgOpUnisntantiate = 5, | 
|  | } entropy_csrng_op_t; | 
|  |  | 
|  | /** | 
|  | * CSRNG application interface command header parameters. | 
|  | */ | 
|  | typedef struct entropy_csrng_cmd { | 
|  | /** | 
|  | * Application command ID. | 
|  | */ | 
|  | entropy_csrng_op_t id; | 
|  | /** | 
|  | * Entropy source enable. | 
|  | * | 
|  | * Mapped to flag0 in the hardware command interface. | 
|  | */ | 
|  | hardened_bool_t disable_trng_input; | 
|  | const entropy_seed_material_t *seed_material; | 
|  | /** | 
|  | * Generate length. Specified as number of 128bit blocks. | 
|  | */ | 
|  | uint32_t generate_len; | 
|  | } entropy_csrng_cmd_t; | 
|  |  | 
|  | #define ENTROPY_CMD(m, i) ((bitfield_field32_t){.mask = m, .index = i}) | 
|  |  | 
|  | OT_WARN_UNUSED_RESULT | 
|  | static status_t csrng_send_app_cmd(uint32_t reg_address, | 
|  | entropy_csrng_cmd_t cmd) { | 
|  | uint32_t reg; | 
|  | bool cmd_ready; | 
|  | do { | 
|  | reg = abs_mmio_read32(kBaseCsrng + CSRNG_SW_CMD_STS_REG_OFFSET); | 
|  | cmd_ready = bitfield_bit32_read(reg, CSRNG_SW_CMD_STS_CMD_RDY_BIT); | 
|  | } while (!cmd_ready); | 
|  |  | 
|  | // The application command header is not specified as a register in the | 
|  | // hardware specification, so the fields are mapped here by hand. The | 
|  | // command register also accepts arbitrary 32bit data. | 
|  | static const bitfield_field32_t kAppCmdFieldFlag0 = ENTROPY_CMD(0xf, 8); | 
|  | static const bitfield_field32_t kAppCmdFieldCmdId = ENTROPY_CMD(0xf, 0); | 
|  | static const bitfield_field32_t kAppCmdFieldCmdLen = ENTROPY_CMD(0xf, 4); | 
|  | static const bitfield_field32_t kAppCmdFieldGlen = ENTROPY_CMD(0x7ffff, 12); | 
|  |  | 
|  | uint32_t cmd_len = cmd.seed_material == NULL ? 0 : cmd.seed_material->len; | 
|  |  | 
|  | if (cmd_len & ~kAppCmdFieldCmdLen.mask) { | 
|  | return INTERNAL(); | 
|  | } | 
|  |  | 
|  | // TODO: Consider removing this since the driver will be constructing these | 
|  | // commands internally. | 
|  | // Ensure the `seed_material` array is word-aligned, so it can be loaded to a | 
|  | // CPU register with natively aligned loads. | 
|  | if (cmd.seed_material != NULL && | 
|  | misalignment32_of((uintptr_t)cmd.seed_material->data) != 0) { | 
|  | return INTERNAL(); | 
|  | } | 
|  |  | 
|  | // Build and write application command header. | 
|  | reg = bitfield_field32_write(0, kAppCmdFieldCmdId, cmd.id); | 
|  | reg = bitfield_field32_write(reg, kAppCmdFieldCmdLen, cmd_len); | 
|  | reg = bitfield_field32_write(reg, kAppCmdFieldGlen, cmd.generate_len); | 
|  |  | 
|  | if (launder32(cmd.disable_trng_input) == kHardenedBoolTrue) { | 
|  | reg = bitfield_field32_write(reg, kAppCmdFieldFlag0, kMultiBitBool4True); | 
|  | } | 
|  |  | 
|  | abs_mmio_write32(reg_address, reg); | 
|  |  | 
|  | for (size_t i = 0; i < cmd_len; ++i) { | 
|  | abs_mmio_write32(reg_address, cmd.seed_material->data[i]); | 
|  | } | 
|  | return OK_STATUS(); | 
|  | } | 
|  |  | 
|  | status_t entropy_csrng_instantiate( | 
|  | hardened_bool_t disable_trng_input, | 
|  | const entropy_seed_material_t *seed_material) { | 
|  | return csrng_send_app_cmd(kBaseCsrng + CSRNG_CMD_REQ_REG_OFFSET, | 
|  | (entropy_csrng_cmd_t){ | 
|  | .id = kEntropyDrbgOpInstantiate, | 
|  | .disable_trng_input = disable_trng_input, | 
|  | .seed_material = seed_material, | 
|  | .generate_len = 0, | 
|  | }); | 
|  | } | 
|  |  | 
|  | status_t entropy_csrng_reseed(hardened_bool_t disable_trng_input, | 
|  | const entropy_seed_material_t *seed_material) { | 
|  | return csrng_send_app_cmd(kBaseCsrng + CSRNG_CMD_REQ_REG_OFFSET, | 
|  | (entropy_csrng_cmd_t){ | 
|  | .id = kEntropyDrbgOpReseed, | 
|  | .disable_trng_input = disable_trng_input, | 
|  | .seed_material = seed_material, | 
|  | .generate_len = 0, | 
|  | }); | 
|  | } | 
|  |  | 
|  | status_t entropy_csrng_update(const entropy_seed_material_t *seed_material) { | 
|  | return csrng_send_app_cmd(kBaseCsrng + CSRNG_CMD_REQ_REG_OFFSET, | 
|  | (entropy_csrng_cmd_t){ | 
|  | .id = kEntropyDrbgOpUpdate, | 
|  | .seed_material = seed_material, | 
|  | .generate_len = 0, | 
|  | }); | 
|  | } | 
|  |  | 
|  | status_t entropy_csrng_generate_start( | 
|  | const entropy_seed_material_t *seed_material, size_t len) { | 
|  | // Round up the number of 128bit blocks. Aligning with respect to uint32_t. | 
|  | // TODO(#6112): Consider using a canonical reference for alignment operations. | 
|  | const uint32_t num_128bit_blocks = (len + 3) / 4; | 
|  | return csrng_send_app_cmd(kBaseCsrng + CSRNG_CMD_REQ_REG_OFFSET, | 
|  | (entropy_csrng_cmd_t){ | 
|  | .id = kEntropyDrbgOpGenerate, | 
|  | .seed_material = seed_material, | 
|  | .generate_len = num_128bit_blocks, | 
|  | }); | 
|  | } | 
|  |  | 
|  | status_t entropy_csrng_generate_data_get(uint32_t *buf, size_t len) { | 
|  | for (size_t i = 0; i < len; ++i) { | 
|  | // Block until there is more data available in the genbits buffer. CSRNG | 
|  | // generates data in 128bit chunks (i.e. 4 words). | 
|  | static_assert(kEntropyCsrngBitsBufferNumWords == 4, | 
|  | "kEntropyCsrngBitsBufferNumWords must be a power of 2."); | 
|  | if (i & (kEntropyCsrngBitsBufferNumWords - 1)) { | 
|  | uint32_t reg; | 
|  | do { | 
|  | reg = abs_mmio_read32(kBaseCsrng + CSRNG_GENBITS_VLD_REG_OFFSET); | 
|  | } while (!bitfield_bit32_read(reg, CSRNG_GENBITS_VLD_GENBITS_VLD_BIT)); | 
|  | } | 
|  | buf[i] = abs_mmio_read32(kBaseCsrng + CSRNG_GENBITS_REG_OFFSET); | 
|  | } | 
|  | return OK_STATUS(); | 
|  | } | 
|  |  | 
|  | status_t entropy_csrng_generate(const entropy_seed_material_t *seed_material, | 
|  | uint32_t *buf, size_t len) { | 
|  | TRY(entropy_csrng_generate_start(seed_material, len)); | 
|  | return entropy_csrng_generate_data_get(buf, len); | 
|  | } | 
|  |  | 
|  | status_t entropy_csrng_uninstantiate(void) { | 
|  | return csrng_send_app_cmd(kBaseCsrng + CSRNG_CMD_REQ_REG_OFFSET, | 
|  | (entropy_csrng_cmd_t){ | 
|  | .id = kEntropyDrbgOpUpdate, | 
|  | .seed_material = NULL, | 
|  | .generate_len = 0, | 
|  | }); | 
|  | } |