|  | // 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 "sw/device/lib/base/multibits.h" | 
|  |  | 
|  | #include "csrng_regs.h"        // Generated | 
|  | #include "edn_regs.h"          // Generated | 
|  | #include "entropy_src_regs.h"  // Generated | 
|  | #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" | 
|  |  | 
|  | enum { | 
|  | kBaseCsrng = TOP_EARLGREY_CSRNG_BASE_ADDR, | 
|  | kBaseEntropySrc = TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR, | 
|  | kBaseEdn0 = TOP_EARLGREY_EDN0_BASE_ADDR, | 
|  | kBaseEdn1 = TOP_EARLGREY_EDN1_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; | 
|  |  | 
|  | /** | 
|  | * Entropy complex configuration modes. | 
|  | * | 
|  | * Each enum value is used a confiugration index in `kEntropyComplexConfigs`. | 
|  | */ | 
|  | typedef enum entropy_complex_config_id { | 
|  | /** | 
|  | * Entropy complex in continuous mode. This is the default runtime | 
|  | * configuration. | 
|  | */ | 
|  | kEntropyComplexConfigIdContinuous, | 
|  | kEntropyComplexConfigIdNumEntries, | 
|  | } entropy_complex_config_id_t; | 
|  |  | 
|  | /** | 
|  | * EDN configuration settings. | 
|  | */ | 
|  | typedef struct edn_config { | 
|  | /** | 
|  | * Base address of the EDN block. | 
|  | */ | 
|  | uint32_t base_address; | 
|  | /** | 
|  | * Number of generate calls between reseed commands. | 
|  | */ | 
|  | uint32_t reseed_interval; | 
|  | /** | 
|  | * Downstream CSRNG instantiate command configuration. | 
|  | */ | 
|  | entropy_csrng_cmd_t instantiate; | 
|  | /** | 
|  | * Downstream CSRNG generate command configuration. | 
|  | */ | 
|  | entropy_csrng_cmd_t generate; | 
|  | /** | 
|  | * Downstream CSRNG reseed command configuration. | 
|  | */ | 
|  | entropy_csrng_cmd_t reseed; | 
|  | } edn_config_t; | 
|  |  | 
|  | /** | 
|  | * Entropy complex configuration settings. | 
|  | * | 
|  | * Contains configuration paramenters for entropy_src, csrng, edn0 and edn1. | 
|  | */ | 
|  | typedef struct entropy_complex_config { | 
|  | /** | 
|  | * Configuration identifier. | 
|  | */ | 
|  | entropy_complex_config_id_t id; | 
|  | /** | 
|  | * If set, FIPS compliant entropy will be generated by this module after being | 
|  | * processed by an SP 800-90B compliant conditioning function. | 
|  | */ | 
|  | multi_bit_bool_t fips_enable; | 
|  | /** | 
|  | * If set, entropy will be routed to a firmware-visible register instead of | 
|  | * being distributed to other hardware IPs. | 
|  | */ | 
|  | multi_bit_bool_t route_to_firmware; | 
|  | /** | 
|  | * If set, raw entropy will be sent to CSRNG, bypassing the conditioner block | 
|  | * and disabling the FIPS hardware generated flag. | 
|  | */ | 
|  | multi_bit_bool_t bypass_conditioner; | 
|  | /** | 
|  | * Enables single bit entropy mode. | 
|  | */ | 
|  | multi_bit_bool_t single_bit_mode; | 
|  | /** | 
|  | * The size of the window used for health tests. | 
|  | */ | 
|  | uint16_t fips_test_window_size; | 
|  | /** | 
|  | * The number of health test failures that must occur before an alert is | 
|  | * triggered. When set to 0, alerts are disabled. | 
|  | */ | 
|  | uint16_t alert_threshold; | 
|  | /** | 
|  | * EDN0 configuration. | 
|  | */ | 
|  | edn_config_t edn0; | 
|  | /** | 
|  | * EDN1 configuration. | 
|  | */ | 
|  | edn_config_t edn1; | 
|  | } entropy_complex_config_t; | 
|  |  | 
|  | // Entropy complex configuration table. This is expected to be fixed at build | 
|  | // time. For this reason, it is not recommended to use this table in a ROM | 
|  | // target unless the values are known to work. In other words, only use in | 
|  | // mutable code partitions. | 
|  | static const entropy_complex_config_t | 
|  | kEntropyComplexConfigs[kEntropyComplexConfigIdNumEntries] = { | 
|  | [kEntropyComplexConfigIdContinuous] = | 
|  | { | 
|  | .fips_enable = kMultiBitBool4True, | 
|  | .route_to_firmware = kMultiBitBool4False, | 
|  | .bypass_conditioner = kMultiBitBool4False, | 
|  | .single_bit_mode = kMultiBitBool4False, | 
|  | .fips_test_window_size = 0x200, | 
|  | .alert_threshold = 2, | 
|  | .edn0 = | 
|  | { | 
|  | .base_address = kBaseEdn0, | 
|  | .reseed_interval = 32, | 
|  | .instantiate = | 
|  | { | 
|  | .id = kEntropyDrbgOpInstantiate, | 
|  | .disable_trng_input = kHardenedBoolFalse, | 
|  | .seed_material = NULL, | 
|  | .generate_len = 0, | 
|  | }, | 
|  | .generate = | 
|  | { | 
|  | .id = kEntropyDrbgOpGenerate, | 
|  | .disable_trng_input = kHardenedBoolFalse, | 
|  | .seed_material = NULL, | 
|  | .generate_len = 8, | 
|  | }, | 
|  | .reseed = | 
|  | { | 
|  | .id = kEntropyDrbgOpReseed, | 
|  | .disable_trng_input = kHardenedBoolFalse, | 
|  | .seed_material = NULL, | 
|  | .generate_len = 0, | 
|  | }, | 
|  | }, | 
|  | .edn1 = | 
|  | { | 
|  | .base_address = kBaseEdn1, | 
|  | .reseed_interval = 4, | 
|  | .instantiate = | 
|  | { | 
|  | .id = kEntropyDrbgOpInstantiate, | 
|  | .disable_trng_input = kHardenedBoolFalse, | 
|  | .seed_material = NULL, | 
|  | .generate_len = 0, | 
|  | }, | 
|  | .generate = | 
|  | { | 
|  | .id = kEntropyDrbgOpGenerate, | 
|  | .seed_material = NULL, | 
|  | .generate_len = 1, | 
|  | }, | 
|  | .reseed = | 
|  | { | 
|  | .id = kEntropyDrbgOpReseed, | 
|  | .disable_trng_input = kHardenedBoolFalse, | 
|  | .seed_material = NULL, | 
|  | .generate_len = 0, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | 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); | 
|  |  | 
|  | #define ENTROPY_CMD(m, i) ((bitfield_field32_t){.mask = m, .index = i}) | 
|  | // 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); | 
|  | #undef ENTROPY_CMD | 
|  |  | 
|  | 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(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Enables the CSRNG block with the SW application and internal state registers | 
|  | * enabled. | 
|  | */ | 
|  | static void csrng_configure(void) { | 
|  | 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); | 
|  | abs_mmio_write32(kBaseCsrng + CSRNG_CTRL_REG_OFFSET, reg); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Stops a given EDN instance. | 
|  | * | 
|  | * It also resets the EDN CSRNG command buffer to avoid synchronization issues | 
|  | * with the upstream CSRNG instance. | 
|  | * | 
|  | * @param edn_address The based address of the target EDN block. | 
|  | */ | 
|  | static void edn_stop(uint32_t edn_address) { | 
|  | // FIFO clear is only honored if edn is enabled. This is needed to avoid | 
|  | // synchronization issues with the upstream CSRNG instance. | 
|  | uint32_t reg = abs_mmio_read32(edn_address + EDN_CTRL_REG_OFFSET); | 
|  | abs_mmio_write32(edn_address + EDN_CTRL_REG_OFFSET, | 
|  | bitfield_field32_write(reg, EDN_CTRL_CMD_FIFO_RST_FIELD, | 
|  | kMultiBitBool4True)); | 
|  |  | 
|  | // Disable EDN and restore the FIFO clear at the same time so that no rogue | 
|  | // command can get in after the clear above. | 
|  | abs_mmio_write32(edn_address + EDN_CTRL_REG_OFFSET, EDN_CTRL_REG_RESVAL); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Blocks until EDN instance is ready to execute a new CSNRG command. | 
|  | * | 
|  | * @param edn_address EDN base address. | 
|  | * @returns an error if the EDN error status bit is set. | 
|  | */ | 
|  | OT_WARN_UNUSED_RESULT | 
|  | static status_t edn_ready_block(uint32_t edn_address) { | 
|  | uint32_t reg; | 
|  | do { | 
|  | reg = abs_mmio_read32(edn_address + EDN_SW_CMD_STS_REG_OFFSET); | 
|  | } while (!bitfield_bit32_read(reg, EDN_SW_CMD_STS_CMD_RDY_BIT)); | 
|  |  | 
|  | if (bitfield_bit32_read(reg, EDN_SW_CMD_STS_CMD_STS_BIT)) { | 
|  | return INTERNAL(); | 
|  | } | 
|  | return OK_STATUS(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Configures EDN instance based on `config` options. | 
|  | * | 
|  | * @param config EDN configuration options. | 
|  | * @returns error on failure. | 
|  | */ | 
|  | OT_WARN_UNUSED_RESULT | 
|  | static status_t edn_configure(const edn_config_t *config) { | 
|  | TRY(csrng_send_app_cmd(config->base_address + EDN_RESEED_CMD_REG_OFFSET, | 
|  | config->reseed)); | 
|  | TRY(csrng_send_app_cmd(config->base_address + EDN_GENERATE_CMD_REG_OFFSET, | 
|  | config->generate)); | 
|  | abs_mmio_write32( | 
|  | config->base_address + EDN_MAX_NUM_REQS_BETWEEN_RESEEDS_REG_OFFSET, | 
|  | config->reseed_interval); | 
|  |  | 
|  | uint32_t reg = | 
|  | bitfield_field32_write(0, EDN_CTRL_EDN_ENABLE_FIELD, kMultiBitBool4True); | 
|  | reg = bitfield_field32_write(reg, EDN_CTRL_AUTO_REQ_MODE_FIELD, | 
|  | kMultiBitBool4True); | 
|  | abs_mmio_write32(config->base_address + EDN_CTRL_REG_OFFSET, reg); | 
|  |  | 
|  | TRY(edn_ready_block(config->base_address)); | 
|  | TRY(csrng_send_app_cmd(config->base_address + EDN_SW_CMD_REQ_REG_OFFSET, | 
|  | config->instantiate)); | 
|  | return edn_ready_block(config->base_address); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Stops the current mode of operation and disables the entropy_src module. | 
|  | * | 
|  | * All configuration registers are set to their reset values to avoid | 
|  | * synchronization issues with internal FIFOs. | 
|  | */ | 
|  | static void entropy_src_stop(void) { | 
|  | abs_mmio_write32(kBaseEntropySrc + ENTROPY_SRC_MODULE_ENABLE_REG_OFFSET, | 
|  | ENTROPY_SRC_MODULE_ENABLE_REG_RESVAL); | 
|  |  | 
|  | // Set default values for other critical registers to avoid synchronization | 
|  | // issues. | 
|  | abs_mmio_write32(kBaseEntropySrc + ENTROPY_SRC_ENTROPY_CONTROL_REG_OFFSET, | 
|  | ENTROPY_SRC_ENTROPY_CONTROL_REG_RESVAL); | 
|  | abs_mmio_write32(kBaseEntropySrc + ENTROPY_SRC_CONF_REG_OFFSET, | 
|  | ENTROPY_SRC_CONF_REG_RESVAL); | 
|  | abs_mmio_write32(kBaseEntropySrc + ENTROPY_SRC_HEALTH_TEST_WINDOWS_REG_OFFSET, | 
|  | ENTROPY_SRC_HEALTH_TEST_WINDOWS_REG_RESVAL); | 
|  | abs_mmio_write32(kBaseEntropySrc + ENTROPY_SRC_ALERT_THRESHOLD_REG_OFFSET, | 
|  | ENTROPY_SRC_ALERT_THRESHOLD_REG_RESVAL); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Disables the entropy complex. | 
|  | * | 
|  | * The order of operations is important to avoid synchronization issues across | 
|  | * blocks. For Example, EDN has FIFOs used to send commands to the downstream | 
|  | * CSRNG instances. Such FIFOs are not cleared when EDN is reconfigured, and an | 
|  | * explicit clear FIFO command needs to be set by software (see #14506). There | 
|  | * may be additional race conditions for downstream blocks that are | 
|  | * processing requests from an upstream endpoint (e.g. entropy_src processing a | 
|  | * request from CSRNG, or CSRNG processing a request from EDN). To avoid these | 
|  | * issues, it is recommended to first disable EDN, then CSRNG and entropy_src | 
|  | * last. | 
|  | * | 
|  | * See hw/ip/csrng/doc/_index.md#module-enable-and-disable for more details. | 
|  | */ | 
|  | static void entropy_complex_stop_all(void) { | 
|  | edn_stop(kBaseEdn0); | 
|  | edn_stop(kBaseEdn1); | 
|  | abs_mmio_write32(kBaseCsrng + CSRNG_CTRL_REG_OFFSET, CSRNG_CTRL_REG_RESVAL); | 
|  | entropy_src_stop(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Configures the entropy_src with based on `config` options. | 
|  | * | 
|  | * @param config Entropy Source configuration options. | 
|  | * @return error on failure. | 
|  | */ | 
|  | OT_WARN_UNUSED_RESULT | 
|  | static status_t entropy_src_configure(const entropy_complex_config_t *config) { | 
|  | // Control register configuration. | 
|  | uint32_t reg = bitfield_field32_write( | 
|  | 0, ENTROPY_SRC_ENTROPY_CONTROL_ES_ROUTE_FIELD, config->route_to_firmware); | 
|  | reg = bitfield_field32_write(reg, ENTROPY_SRC_ENTROPY_CONTROL_ES_TYPE_FIELD, | 
|  | config->bypass_conditioner); | 
|  | abs_mmio_write32(kBaseEntropySrc + ENTROPY_SRC_ENTROPY_CONTROL_REG_OFFSET, | 
|  | reg); | 
|  |  | 
|  | // Config register configuration | 
|  | reg = bitfield_field32_write(0, ENTROPY_SRC_CONF_FIPS_ENABLE_FIELD, | 
|  | config->fips_enable); | 
|  | reg = bitfield_field32_write(reg, | 
|  | ENTROPY_SRC_CONF_ENTROPY_DATA_REG_ENABLE_FIELD, | 
|  | config->route_to_firmware); | 
|  | reg = bitfield_field32_write(reg, ENTROPY_SRC_CONF_THRESHOLD_SCOPE_FIELD, | 
|  | kMultiBitBool4False); | 
|  | reg = bitfield_field32_write(reg, ENTROPY_SRC_CONF_RNG_BIT_ENABLE_FIELD, | 
|  | config->single_bit_mode); | 
|  | reg = bitfield_field32_write(reg, ENTROPY_SRC_CONF_RNG_BIT_SEL_FIELD, 0); | 
|  | abs_mmio_write32(kBaseEntropySrc + ENTROPY_SRC_CONF_REG_OFFSET, reg); | 
|  |  | 
|  | // Configure health test windw. Conditioning bypass is not supported. | 
|  | abs_mmio_write32( | 
|  | kBaseEntropySrc + ENTROPY_SRC_HEALTH_TEST_WINDOWS_REG_OFFSET, | 
|  | bitfield_field32_write(ENTROPY_SRC_HEALTH_TEST_WINDOWS_REG_RESVAL, | 
|  | ENTROPY_SRC_HEALTH_TEST_WINDOWS_FIPS_WINDOW_FIELD, | 
|  | config->fips_test_window_size)); | 
|  |  | 
|  | // Configure alert threshold | 
|  | reg = bitfield_field32_write( | 
|  | 0, ENTROPY_SRC_ALERT_THRESHOLD_ALERT_THRESHOLD_FIELD, | 
|  | config->alert_threshold); | 
|  | reg = bitfield_field32_write( | 
|  | reg, ENTROPY_SRC_ALERT_THRESHOLD_ALERT_THRESHOLD_INV_FIELD, | 
|  | ~config->alert_threshold); | 
|  | abs_mmio_write32(kBaseEntropySrc + ENTROPY_SRC_ALERT_THRESHOLD_REG_OFFSET, | 
|  | reg); | 
|  |  | 
|  | abs_mmio_write32(kBaseEntropySrc + ENTROPY_SRC_MODULE_ENABLE_REG_OFFSET, | 
|  | kMultiBitBool4True); | 
|  |  | 
|  | // TODO: Add FI checks. | 
|  | return OK_STATUS(); | 
|  | } | 
|  |  | 
|  | status_t entropy_complex_init(void) { | 
|  | entropy_complex_stop_all(); | 
|  |  | 
|  | const entropy_complex_config_t *config = | 
|  | &kEntropyComplexConfigs[kEntropyComplexConfigIdContinuous]; | 
|  | if (launder32(config->id) != kEntropyComplexConfigIdContinuous) { | 
|  | return INTERNAL(); | 
|  | } | 
|  |  | 
|  | // TODO: Add health check configuration. | 
|  |  | 
|  | TRY(entropy_src_configure(config)); | 
|  | csrng_configure(); | 
|  | TRY(edn_configure(&config->edn0)); | 
|  | return edn_configure(&config->edn1); | 
|  | } | 
|  |  | 
|  | 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, | 
|  | }); | 
|  | } |