| // 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/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_csrng_result_t write_application_command( |
| const dif_csrng_t *csrng, const csrng_app_cmd_t *cmd) { |
| if (csrng == NULL || cmd == NULL) { |
| return kDifCsrngBadArg; |
| } |
| |
| // 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 kDifCsrngBadArg; |
| } |
| |
| // 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->params.base_addr, CSRNG_CMD_REQ_REG_OFFSET, reg); |
| |
| for (size_t i = 0; i < cmd_len; ++i) { |
| mmio_region_write32(csrng->params.base_addr, CSRNG_CMD_REQ_REG_OFFSET, |
| cmd->seed_material->seed_material[i]); |
| } |
| return kDifCsrngOk; |
| } |
| |
| /** |
| * 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->params.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_csrng_result_t dif_csrng_init(dif_csrng_params_t params, |
| dif_csrng_t *csrng) { |
| if (csrng == NULL) { |
| return kDifCsrngBadArg; |
| } |
| *csrng = (dif_csrng_t){.params = params}; |
| return kDifCsrngOk; |
| } |
| |
| dif_csrng_result_t dif_csrng_configure(const dif_csrng_t *csrng) { |
| if (csrng == NULL) { |
| return kDifCsrngBadArg; |
| } |
| mmio_region_write32(csrng->params.base_addr, CSRNG_CTRL_REG_OFFSET, 1); |
| return kDifCsrngOk; |
| } |
| |
| dif_csrng_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_csrng_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_csrng_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_csrng_result_t dif_csrng_generate_start(const dif_csrng_t *csrng, |
| size_t len) { |
| if (len == 0) { |
| return kDifCsrngBadArg; |
| } |
| |
| // 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_csrng_result_t dif_csrng_generate_end(const dif_csrng_t *csrng, |
| uint32_t *buf, size_t len) { |
| if (csrng == NULL || buf == NULL) { |
| return kDifCsrngBadArg; |
| } |
| |
| // 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->params.base_addr, CSRNG_GENBITS_REG_OFFSET); |
| } |
| return kDifCsrngOk; |
| } |
| |
| dif_csrng_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_csrng_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 kDifCsrngBadArg; |
| } |
| |
| uint32_t reg = |
| mmio_region_read32(csrng->params.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 kDifCsrngOk; |
| } |
| |
| if (cmd_ready) { |
| *status = kDifCsrngCmdStatusReady; |
| return kDifCsrngOk; |
| } |
| |
| *status = kDifCsrngCmdStatusBusy; |
| return kDifCsrngOk; |
| } |
| |
| dif_csrng_result_t dif_csrng_get_output_status( |
| const dif_csrng_t *csrng, dif_csrng_output_status_t *status) { |
| if (csrng == NULL || status == NULL) { |
| return kDifCsrngBadArg; |
| } |
| get_output_status(csrng, status); |
| return kDifCsrngOk; |
| } |