| // 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 "sw/device/lib/dif/dif_csrng_shared.h" |
| |
| #include "csrng_regs.h" // Generated |
| |
| /** |
| * 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 void spin_until_ready(const dif_csrng_t *csrng) { |
| dif_csrng_output_status_t status; |
| do { |
| get_output_status(csrng, &status); |
| } while (!status.valid_data); |
| } |
| |
| static dif_result_t check_locked(const dif_csrng_t *csrng) { |
| if (mmio_region_read32(csrng->base_addr, CSRNG_REGWEN_REG_OFFSET) == 0) { |
| return kDifLocked; |
| } |
| return kDifOk; |
| } |
| |
| dif_result_t dif_csrng_configure(const dif_csrng_t *csrng) { |
| if (csrng == NULL) { |
| return kDifBadArg; |
| } |
| DIF_RETURN_IF_ERROR(check_locked(csrng)); |
| |
| uint32_t reg = |
| bitfield_field32_write(0, CSRNG_CTRL_ENABLE_FIELD, kMultiBitBool4True); |
| reg = bitfield_field32_write(reg, CSRNG_CTRL_SW_APP_ENABLE_FIELD, |
| kMultiBitBool4True); |
| reg = bitfield_field32_write(reg, CSRNG_CTRL_READ_INT_STATE_FIELD, |
| kMultiBitBool4True); |
| mmio_region_write32(csrng->base_addr, CSRNG_CTRL_REG_OFFSET, reg); |
| 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) { |
| if (csrng == NULL || seed_material == NULL) { |
| return kDifBadArg; |
| } |
| return csrng_send_app_cmd(csrng->base_addr, CSRNG_CMD_REQ_REG_OFFSET, |
| (csrng_app_cmd_t){ |
| .id = kCsrngAppCmdInstantiate, |
| .entropy_src_enable = entropy_src_enable, |
| .seed_material = seed_material, |
| }); |
| } |
| |
| dif_result_t dif_csrng_reseed(const dif_csrng_t *csrng, |
| const dif_csrng_seed_material_t *seed_material) { |
| if (csrng == NULL || seed_material == NULL) { |
| return kDifBadArg; |
| } |
| return csrng_send_app_cmd(csrng->base_addr, CSRNG_CMD_REQ_REG_OFFSET, |
| (csrng_app_cmd_t){ |
| .id = kCsrngAppCmdReseed, |
| .seed_material = seed_material, |
| }); |
| } |
| |
| dif_result_t dif_csrng_update(const dif_csrng_t *csrng, |
| const dif_csrng_seed_material_t *seed_material) { |
| if (csrng == NULL || seed_material == NULL) { |
| return kDifBadArg; |
| } |
| return csrng_send_app_cmd(csrng->base_addr, CSRNG_CMD_REQ_REG_OFFSET, |
| (csrng_app_cmd_t){ |
| .id = kCsrngAppCmdUpdate, |
| .seed_material = seed_material, |
| }); |
| } |
| |
| dif_result_t dif_csrng_generate_start(const dif_csrng_t *csrng, size_t len) { |
| if (csrng == NULL || 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; |
| return csrng_send_app_cmd(csrng->base_addr, CSRNG_CMD_REQ_REG_OFFSET, |
| (csrng_app_cmd_t){ |
| .id = kCsrngAppCmdGenerate, |
| .generate_len = num_128bit_blocks, |
| }); |
| } |
| |
| dif_result_t dif_csrng_generate_read(const dif_csrng_t *csrng, uint32_t *buf, |
| size_t len) { |
| if (csrng == NULL || buf == NULL) { |
| return kDifBadArg; |
| } |
| |
| for (size_t i = 0; i < len; ++i) { |
| // Block until there is more data available in the genbits buffer. |
| if (i % kCsrngGenBitsBufferSize == 0) { |
| spin_until_ready(csrng); |
| } |
| 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) { |
| if (csrng == NULL) { |
| return kDifBadArg; |
| } |
| return csrng_send_app_cmd(csrng->base_addr, CSRNG_CMD_REQ_REG_OFFSET, |
| (csrng_app_cmd_t){ |
| .id = kCsrngAppCmdUnisntantiate, |
| }); |
| } |
| |
| 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; |
| } |
| *status = (dif_csrng_cmd_status_t){0}; |
| |
| 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->kind = kDifCsrngCmdStatusError; |
| uint32_t reg = |
| mmio_region_read32(csrng->base_addr, CSRNG_ERR_CODE_REG_OFFSET); |
| |
| status->unhealthy_fifos = |
| bitfield_bit32_copy(status->unhealthy_fifos, kDifCsrngFifoCmd, reg, |
| CSRNG_ERR_CODE_SFIFO_CMD_ERR_BIT); |
| status->unhealthy_fifos = |
| bitfield_bit32_copy(status->unhealthy_fifos, kDifCsrngFifoGenBits, reg, |
| CSRNG_ERR_CODE_SFIFO_GENBITS_ERR_BIT); |
| status->unhealthy_fifos = |
| bitfield_bit32_copy(status->unhealthy_fifos, kDifCsrngFifoCmdReq, reg, |
| CSRNG_ERR_CODE_SFIFO_CMDREQ_ERR_BIT); |
| status->unhealthy_fifos = |
| bitfield_bit32_copy(status->unhealthy_fifos, kDifCsrngFifoRcStage, reg, |
| CSRNG_ERR_CODE_SFIFO_RCSTAGE_ERR_BIT); |
| status->unhealthy_fifos = |
| bitfield_bit32_copy(status->unhealthy_fifos, kDifCsrngFifoKeyVrc, reg, |
| CSRNG_ERR_CODE_SFIFO_KEYVRC_ERR_BIT); |
| status->unhealthy_fifos = |
| bitfield_bit32_copy(status->unhealthy_fifos, kDifCsrngFifoUpdateReq, |
| reg, CSRNG_ERR_CODE_SFIFO_UPDREQ_ERR_BIT); |
| status->unhealthy_fifos = |
| bitfield_bit32_copy(status->unhealthy_fifos, kDifCsrngFifoBencRec, reg, |
| CSRNG_ERR_CODE_SFIFO_BENCREQ_ERR_BIT); |
| status->unhealthy_fifos = |
| bitfield_bit32_copy(status->unhealthy_fifos, kDifCsrngFifoBencAck, reg, |
| CSRNG_ERR_CODE_SFIFO_BENCACK_ERR_BIT); |
| status->unhealthy_fifos = |
| bitfield_bit32_copy(status->unhealthy_fifos, kDifCsrngFifoPData, reg, |
| CSRNG_ERR_CODE_SFIFO_PDATA_ERR_BIT); |
| status->unhealthy_fifos = |
| bitfield_bit32_copy(status->unhealthy_fifos, kDifCsrngFifoFinal, reg, |
| CSRNG_ERR_CODE_SFIFO_FINAL_ERR_BIT); |
| status->unhealthy_fifos = |
| bitfield_bit32_copy(status->unhealthy_fifos, kDifCsrngFifoGBencAck, reg, |
| CSRNG_ERR_CODE_SFIFO_GBENCACK_ERR_BIT); |
| status->unhealthy_fifos = |
| bitfield_bit32_copy(status->unhealthy_fifos, kDifCsrngFifoGrcStage, reg, |
| CSRNG_ERR_CODE_SFIFO_GRCSTAGE_ERR_BIT); |
| status->unhealthy_fifos = |
| bitfield_bit32_copy(status->unhealthy_fifos, kDifCsrngFifoGGenReq, reg, |
| CSRNG_ERR_CODE_SFIFO_GGENREQ_ERR_BIT); |
| status->unhealthy_fifos = |
| bitfield_bit32_copy(status->unhealthy_fifos, kDifCsrngFifoGadStage, reg, |
| CSRNG_ERR_CODE_SFIFO_GADSTAGE_ERR_BIT); |
| status->unhealthy_fifos = |
| bitfield_bit32_copy(status->unhealthy_fifos, kDifCsrngFifoBlockEnc, reg, |
| CSRNG_ERR_CODE_SFIFO_BLKENC_ERR_BIT); |
| |
| status->errors = |
| bitfield_bit32_copy(status->errors, kDifCsrngErrorCmdStageSm, reg, |
| CSRNG_ERR_CODE_CMD_STAGE_SM_ERR_BIT); |
| status->errors = bitfield_bit32_copy(status->errors, kDifCsrngErrorMainSm, |
| reg, CSRNG_ERR_CODE_MAIN_SM_ERR_BIT); |
| status->errors = |
| bitfield_bit32_copy(status->errors, kDifCsrngErrorDrbgGenSm, reg, |
| CSRNG_ERR_CODE_DRBG_GEN_SM_ERR_BIT); |
| status->errors = |
| bitfield_bit32_copy(status->errors, kDifCsrngErrorDrbgUpdateBlockEncSm, |
| reg, CSRNG_ERR_CODE_DRBG_UPDBE_SM_ERR_BIT); |
| status->errors = |
| bitfield_bit32_copy(status->errors, kDifCsrngErrorDrbgUpdateOutBlockSm, |
| reg, CSRNG_ERR_CODE_DRBG_UPDOB_SM_ERR_BIT); |
| status->errors = |
| bitfield_bit32_copy(status->errors, kDifCsrngErrorAesSm, reg, |
| CSRNG_ERR_CODE_AES_CIPHER_SM_ERR_BIT); |
| status->errors = |
| bitfield_bit32_copy(status->errors, kDifCsrngErrorGenerateCmdCounter, |
| reg, CSRNG_ERR_CODE_CMD_GEN_CNT_ERR_BIT); |
| status->errors = |
| bitfield_bit32_copy(status->errors, kDifCsrngErrorFifoWrite, reg, |
| CSRNG_ERR_CODE_FIFO_WRITE_ERR_BIT); |
| status->errors = bitfield_bit32_copy(status->errors, kDifCsrngErrorFifoRead, |
| reg, CSRNG_ERR_CODE_FIFO_READ_ERR_BIT); |
| status->errors = |
| bitfield_bit32_copy(status->errors, kDifCsrngErrorFifoFullAndEmpty, reg, |
| CSRNG_ERR_CODE_FIFO_STATE_ERR_BIT); |
| |
| return kDifOk; |
| } |
| |
| if (cmd_ready) { |
| status->kind = kDifCsrngCmdStatusReady; |
| return kDifOk; |
| } |
| |
| status->kind = kDifCsrngCmdStatusBusy; |
| return kDifOk; |
| } |
| |
| dif_result_t dif_csrng_get_cmd_force_unhealthy_fifo(const dif_csrng_t *csrng, |
| dif_csrng_fifo_t fifo) { |
| if (csrng == NULL) { |
| return kDifBadArg; |
| } |
| |
| uint32_t fifo_bit; |
| switch (fifo) { |
| case kDifCsrngFifoCmd: |
| fifo_bit = CSRNG_ERR_CODE_SFIFO_CMD_ERR_BIT; |
| break; |
| case kDifCsrngFifoGenBits: |
| fifo_bit = CSRNG_ERR_CODE_SFIFO_GENBITS_ERR_BIT; |
| break; |
| case kDifCsrngFifoCmdReq: |
| fifo_bit = CSRNG_ERR_CODE_SFIFO_CMDREQ_ERR_BIT; |
| break; |
| case kDifCsrngFifoRcStage: |
| fifo_bit = CSRNG_ERR_CODE_SFIFO_RCSTAGE_ERR_BIT; |
| break; |
| case kDifCsrngFifoKeyVrc: |
| fifo_bit = CSRNG_ERR_CODE_SFIFO_KEYVRC_ERR_BIT; |
| break; |
| case kDifCsrngFifoUpdateReq: |
| fifo_bit = CSRNG_ERR_CODE_SFIFO_UPDREQ_ERR_BIT; |
| break; |
| case kDifCsrngFifoBencRec: |
| fifo_bit = CSRNG_ERR_CODE_SFIFO_BENCREQ_ERR_BIT; |
| break; |
| case kDifCsrngFifoBencAck: |
| fifo_bit = CSRNG_ERR_CODE_SFIFO_BENCACK_ERR_BIT; |
| break; |
| case kDifCsrngFifoPData: |
| fifo_bit = CSRNG_ERR_CODE_SFIFO_PDATA_ERR_BIT; |
| break; |
| case kDifCsrngFifoFinal: |
| fifo_bit = CSRNG_ERR_CODE_SFIFO_FINAL_ERR_BIT; |
| break; |
| case kDifCsrngFifoGBencAck: |
| fifo_bit = CSRNG_ERR_CODE_SFIFO_GBENCACK_ERR_BIT; |
| break; |
| case kDifCsrngFifoGrcStage: |
| fifo_bit = CSRNG_ERR_CODE_SFIFO_GRCSTAGE_ERR_BIT; |
| break; |
| case kDifCsrngFifoGGenReq: |
| fifo_bit = CSRNG_ERR_CODE_SFIFO_GGENREQ_ERR_BIT; |
| break; |
| case kDifCsrngFifoGadStage: |
| fifo_bit = CSRNG_ERR_CODE_SFIFO_GADSTAGE_ERR_BIT; |
| break; |
| case kDifCsrngFifoBlockEnc: |
| fifo_bit = CSRNG_ERR_CODE_SFIFO_BLKENC_ERR_BIT; |
| break; |
| default: |
| return kDifBadArg; |
| } |
| |
| DIF_RETURN_IF_ERROR(check_locked(csrng)); |
| mmio_region_write32(csrng->base_addr, CSRNG_ERR_CODE_TEST_REG_OFFSET, |
| fifo_bit); |
| return kDifOk; |
| } |
| |
| dif_result_t dif_csrng_get_cmd_force_error(const dif_csrng_t *csrng, |
| dif_csrng_error_t error) { |
| if (csrng == NULL) { |
| return kDifBadArg; |
| } |
| |
| uint32_t error_bit; |
| switch (error) { |
| case kDifCsrngErrorCmdStageSm: |
| error_bit = CSRNG_ERR_CODE_CMD_STAGE_SM_ERR_BIT; |
| break; |
| case kDifCsrngErrorMainSm: |
| error_bit = CSRNG_ERR_CODE_MAIN_SM_ERR_BIT; |
| break; |
| case kDifCsrngErrorDrbgGenSm: |
| error_bit = CSRNG_ERR_CODE_DRBG_GEN_SM_ERR_BIT; |
| break; |
| case kDifCsrngErrorDrbgUpdateBlockEncSm: |
| error_bit = CSRNG_ERR_CODE_DRBG_UPDBE_SM_ERR_BIT; |
| break; |
| case kDifCsrngErrorDrbgUpdateOutBlockSm: |
| error_bit = CSRNG_ERR_CODE_DRBG_UPDOB_SM_ERR_BIT; |
| break; |
| case kDifCsrngErrorAesSm: |
| error_bit = CSRNG_ERR_CODE_AES_CIPHER_SM_ERR_BIT; |
| break; |
| case kDifCsrngErrorGenerateCmdCounter: |
| error_bit = CSRNG_ERR_CODE_CMD_GEN_CNT_ERR_BIT; |
| break; |
| case kDifCsrngErrorFifoWrite: |
| error_bit = CSRNG_ERR_CODE_FIFO_WRITE_ERR_BIT; |
| break; |
| case kDifCsrngErrorFifoRead: |
| error_bit = CSRNG_ERR_CODE_FIFO_READ_ERR_BIT; |
| break; |
| case kDifCsrngErrorFifoFullAndEmpty: |
| error_bit = CSRNG_ERR_CODE_FIFO_STATE_ERR_BIT; |
| break; |
| default: |
| return kDifBadArg; |
| } |
| |
| DIF_RETURN_IF_ERROR(check_locked(csrng)); |
| mmio_region_write32(csrng->base_addr, CSRNG_ERR_CODE_TEST_REG_OFFSET, |
| error_bit); |
| return kDifOk; |
| } |
| |
| dif_result_t dif_csrng_get_main_state_machine(const dif_csrng_t *csrng, |
| uint32_t *state) { |
| if (csrng == NULL || state == NULL) { |
| return kDifBadArg; |
| } |
| |
| *state = mmio_region_read32(csrng->base_addr, CSRNG_MAIN_SM_STATE_REG_OFFSET); |
| return kDifOk; |
| } |
| |
| dif_result_t dif_csrng_get_hw_csrng_exceptions(const dif_csrng_t *csrng, |
| uint32_t *exceptions) { |
| if (csrng == NULL || exceptions == NULL) { |
| return kDifBadArg; |
| } |
| |
| *exceptions = |
| mmio_region_read32(csrng->base_addr, CSRNG_HW_EXC_STS_REG_OFFSET); |
| return kDifOk; |
| } |
| |
| dif_result_t dif_csrng_clear_hw_csrng_exceptions(const dif_csrng_t *csrng) { |
| if (csrng == NULL) { |
| return kDifBadArg; |
| } |
| |
| mmio_region_write32(csrng->base_addr, CSRNG_HW_EXC_STS_REG_OFFSET, 0); |
| 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; |
| } |
| |
| 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_lock(const dif_csrng_t *csrng) { |
| if (csrng == NULL) { |
| return kDifBadArg; |
| } |
| mmio_region_write32(csrng->base_addr, CSRNG_REGWEN_REG_OFFSET, 0); |
| return kDifOk; |
| } |
| |
| dif_result_t dif_csrng_is_locked(const dif_csrng_t *csrng, bool *is_locked) { |
| if (csrng == NULL || is_locked == NULL) { |
| return kDifBadArg; |
| } |
| *is_locked = check_locked(csrng) != kDifOk; |
| return kDifOk; |
| } |
| |
| dif_result_t dif_csrng_stop(const dif_csrng_t *csrng) { |
| if (csrng == NULL) { |
| return kDifBadArg; |
| } |
| DIF_RETURN_IF_ERROR(check_locked(csrng)); |
| mmio_region_write32(csrng->base_addr, CSRNG_CTRL_REG_OFFSET, |
| CSRNG_CTRL_REG_RESVAL); |
| return kDifOk; |
| } |
| |
| dif_result_t dif_csrng_get_recoverable_alerts(const dif_csrng_t *csrng, |
| uint32_t *alerts) { |
| if (csrng == NULL || alerts == NULL) { |
| return kDifBadArg; |
| } |
| |
| *alerts = 0; |
| uint32_t reg = |
| mmio_region_read32(csrng->base_addr, CSRNG_RECOV_ALERT_STS_REG_OFFSET); |
| *alerts = |
| bitfield_bit32_copy(*alerts, kDifCsrngRecoverableAlertBadEnable, reg, |
| CSRNG_RECOV_ALERT_STS_ENABLE_FIELD_ALERT_BIT); |
| *alerts = |
| bitfield_bit32_copy(*alerts, kDifCsrngRecoverableAlertBadSwAppEnable, reg, |
| CSRNG_RECOV_ALERT_STS_SW_APP_ENABLE_FIELD_ALERT_BIT); |
| *alerts = |
| bitfield_bit32_copy(*alerts, kDifCsrngRecoverableAlertBadIntState, reg, |
| CSRNG_RECOV_ALERT_STS_READ_INT_STATE_FIELD_ALERT_BIT); |
| *alerts = |
| bitfield_bit32_copy(*alerts, kDifCsrngRecoverableAlertRepeatedGenBits, |
| reg, CSRNG_RECOV_ALERT_STS_CS_BUS_CMP_ALERT_BIT); |
| return kDifOk; |
| } |
| |
| dif_result_t dif_csrng_clear_recoverable_alerts(const dif_csrng_t *csrng) { |
| if (csrng == NULL) { |
| return kDifBadArg; |
| } |
| mmio_region_write32(csrng->base_addr, CSRNG_RECOV_ALERT_STS_REG_OFFSET, 0); |
| return kDifOk; |
| } |