|  | // 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/dif/dif_csrng.h" | 
|  |  | 
|  | #include "sw/device/lib/base/bitfield.h" | 
|  | #include "sw/device/lib/base/macros.h" | 
|  | #include "sw/device/lib/base/memory.h" | 
|  | #include "sw/device/lib/base/mmio.h" | 
|  |  | 
|  | #include "csrng_regs.h"  // Generated | 
|  |  | 
|  | enum { | 
|  | /** | 
|  | * CSRNG genbits buffer size in uint32_t words. | 
|  | */ | 
|  | kCsrngGenBitsBufferSize = 4, | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Supported CSRNG application commands. | 
|  | * See https://docs.opentitan.org/hw/ip/csrng/doc/#command-header for | 
|  | * details. | 
|  | */ | 
|  | typedef enum csrng_app_cmd_id { | 
|  | kCsrngAppCmdInstantiate = 1, | 
|  | kCsrngAppCmdReseed = 2, | 
|  | kCsrngAppCmdGenerate = 3, | 
|  | kCsrngAppCmdUpdate = 4, | 
|  | kCsrngAppCmdUnisntantiate = 5, | 
|  | } csrng_app_cmd_id_t; | 
|  |  | 
|  | /** | 
|  | * CSRNG application interface command header parameters. | 
|  | */ | 
|  | typedef struct csrng_app_cmd { | 
|  | /** | 
|  | * Application command. | 
|  | */ | 
|  | csrng_app_cmd_id_t id; | 
|  | /** | 
|  | * Entropy source enable. | 
|  | * | 
|  | * Mapped to flag0 in the hardware command interface. | 
|  | */ | 
|  | dif_csrng_entropy_src_toggle_t entropy_src_enable; | 
|  | /** | 
|  | * Seed material. Only used in `kCsrngAppCmdInstantiate`, `kCsrngAppCmdReseed` | 
|  | * and `kCsrngAppCmdUpdate` commands. | 
|  | */ | 
|  | const dif_csrng_seed_material_t *seed_material; | 
|  | /** | 
|  | * Generate length. Specified as number of 128bit blocks. | 
|  | */ | 
|  | uint32_t generate_len; | 
|  | } csrng_app_cmd_t; | 
|  |  | 
|  | /** | 
|  | * Writes application command `cmd` to the CSRNG_CMD_REQ_REG register. | 
|  | * Returns the result of the operation. | 
|  | */ | 
|  | static dif_result_t write_application_command(const dif_csrng_t *csrng, | 
|  | const csrng_app_cmd_t *cmd) { | 
|  | if (csrng == NULL || cmd == NULL) { | 
|  | return kDifBadArg; | 
|  | } | 
|  |  | 
|  | // 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. | 
|  | const uint32_t kAppCmdBitFlag0 = 8; | 
|  | const bitfield_field32_t kAppCmdFieldCmdId = {.mask = 0xf, .index = 0}; | 
|  | const bitfield_field32_t kAppCmdFieldCmdLen = {.mask = 0xf, .index = 4}; | 
|  | const bitfield_field32_t kAppCmdFieldGlen = {.mask = 0x7ffff, .index = 12}; | 
|  |  | 
|  | uint32_t cmd_len = | 
|  | cmd->seed_material == NULL ? 0 : cmd->seed_material->seed_material_len; | 
|  |  | 
|  | if (cmd_len & ~kAppCmdFieldCmdLen.mask) { | 
|  | return kDifBadArg; | 
|  | } | 
|  |  | 
|  | // Build and write application command header. | 
|  | uint32_t reg = bitfield_field32_write(0, kAppCmdFieldCmdId, cmd->id); | 
|  | reg = bitfield_field32_write(reg, kAppCmdFieldCmdLen, cmd_len); | 
|  | reg = bitfield_bit32_write(reg, kAppCmdBitFlag0, cmd->entropy_src_enable); | 
|  | reg = bitfield_field32_write(reg, kAppCmdFieldGlen, cmd->generate_len); | 
|  | mmio_region_write32(csrng->base_addr, CSRNG_CMD_REQ_REG_OFFSET, reg); | 
|  |  | 
|  | for (size_t i = 0; i < cmd_len; ++i) { | 
|  | mmio_region_write32(csrng->base_addr, CSRNG_CMD_REQ_REG_OFFSET, | 
|  | cmd->seed_material->seed_material[i]); | 
|  | } | 
|  | return kDifOk; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Reads the output data register status. | 
|  | */ | 
|  | static void get_output_status(const dif_csrng_t *csrng, | 
|  | dif_csrng_output_status_t *status) { | 
|  | uint32_t reg = | 
|  | mmio_region_read32(csrng->base_addr, CSRNG_GENBITS_VLD_REG_OFFSET); | 
|  | status->valid_data = | 
|  | bitfield_bit32_read(reg, CSRNG_GENBITS_VLD_GENBITS_VLD_BIT); | 
|  | status->fips_mode = | 
|  | bitfield_bit32_read(reg, CSRNG_GENBITS_VLD_GENBITS_FIPS_BIT); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns true if the data register has valid data. | 
|  | */ | 
|  | static bool is_output_ready(const dif_csrng_t *csrng) { | 
|  | dif_csrng_output_status_t status; | 
|  | get_output_status(csrng, &status); | 
|  | return status.valid_data; | 
|  | } | 
|  |  | 
|  | dif_result_t dif_csrng_configure(const dif_csrng_t *csrng) { | 
|  | if (csrng == NULL) { | 
|  | return kDifBadArg; | 
|  | } | 
|  | mmio_region_write32(csrng->base_addr, CSRNG_CTRL_REG_OFFSET, 0xaaa); | 
|  | return kDifOk; | 
|  | } | 
|  |  | 
|  | dif_result_t dif_csrng_instantiate( | 
|  | const dif_csrng_t *csrng, dif_csrng_entropy_src_toggle_t entropy_src_enable, | 
|  | const dif_csrng_seed_material_t *seed_material) { | 
|  | const csrng_app_cmd_t app_cmd = { | 
|  | .id = kCsrngAppCmdInstantiate, | 
|  | .entropy_src_enable = entropy_src_enable, | 
|  | .seed_material = seed_material, | 
|  | .generate_len = 0, | 
|  | }; | 
|  | return write_application_command(csrng, &app_cmd); | 
|  | } | 
|  |  | 
|  | dif_result_t dif_csrng_reseed(const dif_csrng_t *csrng, | 
|  | const dif_csrng_seed_material_t *seed_material) { | 
|  | const csrng_app_cmd_t app_cmd = { | 
|  | .id = kCsrngAppCmdReseed, | 
|  | .entropy_src_enable = false, | 
|  | .seed_material = seed_material, | 
|  | .generate_len = 0, | 
|  | }; | 
|  | return write_application_command(csrng, &app_cmd); | 
|  | } | 
|  |  | 
|  | dif_result_t dif_csrng_update(const dif_csrng_t *csrng, | 
|  | const dif_csrng_seed_material_t *seed_material) { | 
|  | const csrng_app_cmd_t app_cmd = { | 
|  | .id = kCsrngAppCmdUpdate, | 
|  | .entropy_src_enable = false, | 
|  | .seed_material = seed_material, | 
|  | .generate_len = 0, | 
|  | }; | 
|  | return write_application_command(csrng, &app_cmd); | 
|  | } | 
|  |  | 
|  | dif_result_t dif_csrng_generate_start(const dif_csrng_t *csrng, size_t len) { | 
|  | if (len == 0) { | 
|  | return kDifBadArg; | 
|  | } | 
|  |  | 
|  | // 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; | 
|  |  | 
|  | const csrng_app_cmd_t app_cmd = { | 
|  | .id = kCsrngAppCmdGenerate, | 
|  | .entropy_src_enable = false, | 
|  | .seed_material = NULL, | 
|  | .generate_len = num_128bit_blocks, | 
|  | }; | 
|  | return write_application_command(csrng, &app_cmd); | 
|  | } | 
|  |  | 
|  | dif_result_t dif_csrng_generate_end(const dif_csrng_t *csrng, uint32_t *buf, | 
|  | size_t len) { | 
|  | if (csrng == NULL || buf == NULL) { | 
|  | return kDifBadArg; | 
|  | } | 
|  |  | 
|  | // Wait until there is data ready. | 
|  | while (!is_output_ready(csrng)) { | 
|  | } | 
|  |  | 
|  | for (size_t i = 0, rd_cnt = 0; i < len; ++i, ++rd_cnt) { | 
|  | // Block until there is more data available in the genbits buffer. | 
|  | if (rd_cnt == kCsrngGenBitsBufferSize) { | 
|  | while (!is_output_ready(csrng)) { | 
|  | } | 
|  | rd_cnt = 0; | 
|  | } | 
|  | buf[i] = mmio_region_read32(csrng->base_addr, CSRNG_GENBITS_REG_OFFSET); | 
|  | } | 
|  | return kDifOk; | 
|  | } | 
|  |  | 
|  | dif_result_t dif_csrng_uninstantiate(const dif_csrng_t *csrng) { | 
|  | const csrng_app_cmd_t app_cmd = { | 
|  | .id = kCsrngAppCmdUnisntantiate, | 
|  | .entropy_src_enable = false, | 
|  | .seed_material = NULL, | 
|  | .generate_len = 0, | 
|  | }; | 
|  | return write_application_command(csrng, &app_cmd); | 
|  | } | 
|  |  | 
|  | dif_result_t dif_csrng_get_cmd_interface_status( | 
|  | const dif_csrng_t *csrng, dif_csrng_cmd_status_t *status) { | 
|  | if (csrng == NULL || status == NULL) { | 
|  | return kDifBadArg; | 
|  | } | 
|  |  | 
|  | uint32_t reg = | 
|  | mmio_region_read32(csrng->base_addr, CSRNG_SW_CMD_STS_REG_OFFSET); | 
|  | bool cmd_ready = bitfield_bit32_read(reg, CSRNG_SW_CMD_STS_CMD_RDY_BIT); | 
|  | bool cmd_error = bitfield_bit32_read(reg, CSRNG_SW_CMD_STS_CMD_STS_BIT); | 
|  |  | 
|  | // The function prioritizes error detection to avoid masking errors | 
|  | // when `cmd_ready` is set to true. | 
|  | if (cmd_error) { | 
|  | *status = kDifCsrngCmdStatusError; | 
|  | return kDifOk; | 
|  | } | 
|  |  | 
|  | if (cmd_ready) { | 
|  | *status = kDifCsrngCmdStatusReady; | 
|  | return kDifOk; | 
|  | } | 
|  |  | 
|  | *status = kDifCsrngCmdStatusBusy; | 
|  | return kDifOk; | 
|  | } | 
|  |  | 
|  | dif_result_t dif_csrng_get_output_status(const dif_csrng_t *csrng, | 
|  | dif_csrng_output_status_t *status) { | 
|  | if (csrng == NULL || status == NULL) { | 
|  | return kDifBadArg; | 
|  | } | 
|  | get_output_status(csrng, status); | 
|  | return kDifOk; | 
|  | } | 
|  |  | 
|  | OT_WARN_UNUSED_RESULT | 
|  | dif_result_t dif_csrng_get_internal_state( | 
|  | const dif_csrng_t *csrng, dif_csrng_internal_state_id_t instance_id, | 
|  | dif_csrng_internal_state_t *state) { | 
|  | if (csrng == NULL || state == NULL) { | 
|  | return kDifBadArg; | 
|  | } | 
|  |  | 
|  | // Select the instance id to read the internal state from, request a state | 
|  | // machine halt, and wait for the internal registers to be ready to be read. | 
|  | uint32_t reg = bitfield_field32_write( | 
|  | 0, CSRNG_INT_STATE_NUM_INT_STATE_NUM_FIELD, instance_id); | 
|  | mmio_region_write32(csrng->base_addr, CSRNG_INT_STATE_NUM_REG_OFFSET, reg); | 
|  |  | 
|  | // Read the internal state. | 
|  | state->reseed_counter = | 
|  | mmio_region_read32(csrng->base_addr, CSRNG_INT_STATE_VAL_REG_OFFSET); | 
|  |  | 
|  | for (size_t i = 0; i < ARRAYSIZE(state->v); ++i) { | 
|  | state->v[i] = | 
|  | mmio_region_read32(csrng->base_addr, CSRNG_INT_STATE_VAL_REG_OFFSET); | 
|  | } | 
|  |  | 
|  | for (size_t i = 0; i < ARRAYSIZE(state->key); ++i) { | 
|  | state->key[i] = | 
|  | mmio_region_read32(csrng->base_addr, CSRNG_INT_STATE_VAL_REG_OFFSET); | 
|  | } | 
|  |  | 
|  | uint32_t flags = | 
|  | mmio_region_read32(csrng->base_addr, CSRNG_INT_STATE_VAL_REG_OFFSET); | 
|  |  | 
|  | // The following bit indexes are defined in | 
|  | // https://docs.opentitan.org/hw/ip/csrng/doc/#working-state-values | 
|  | state->instantiated = bitfield_bit32_read(flags, /*bit_index=*/0u); | 
|  | state->fips_compliance = bitfield_bit32_read(flags, /*bit_index=*/1u); | 
|  |  | 
|  | return kDifOk; | 
|  | } | 
|  |  | 
|  | dif_result_t dif_csrng_stop(const dif_csrng_t *csrng) { | 
|  | if (csrng == NULL) { | 
|  | return kDifBadArg; | 
|  | } | 
|  |  | 
|  | mmio_region_write32(csrng->base_addr, CSRNG_CTRL_REG_OFFSET, | 
|  | CSRNG_CTRL_REG_RESVAL); | 
|  |  | 
|  | return kDifOk; | 
|  | } |