blob: 7f813f3ab8aaa5686caaec976e885fe539ee4001 [file] [log] [blame]
// 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,
});
}