blob: 626d3f8815450a8faa953a3043ad389eb1c3fb3e [file] [log] [blame]
/*
* Copyright (c) 2023 Microchip Technology Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT microchip_xec_symcr
#include <errno.h>
#include <string.h>
#include <zephyr/kernel.h>
#include <zephyr/crypto/crypto.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/mchp_xec_clock_control.h>
#include "zephyr/sys/util.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(xec_symcr, CONFIG_CRYPTO_LOG_LEVEL);
#include <soc.h>
/* ROM API for Hash without using external files */
enum mchp_rom_hash_alg_id {
MCHP_ROM_HASH_ALG_NONE = 0,
MCHP_ROM_HASH_ALG_SHA1,
MCHP_ROM_HASH_ALG_SHA224,
MCHP_ROM_HASH_ALG_SHA256,
MCHP_ROM_HASH_ALG_SHA384,
MCHP_ROM_HASH_ALG_SHA512,
MCHP_ROM_HASH_ALG_SM3,
MCHP_ROM_HASH_ALG_MAX
};
#define MCHP_XEC_STRUCT_HASH_STATE_STRUCT_SIZE 8
#define MCHP_XEC_STRUCT_HASH_STRUCT_SIZE 240
struct mchphashstate {
uint32_t v[MCHP_XEC_STRUCT_HASH_STATE_STRUCT_SIZE / 4];
};
struct mchphash {
uint32_t v[MCHP_XEC_STRUCT_HASH_STRUCT_SIZE / 4];
};
#define MCHP_XEC_ROM_API_BASE DT_REG_ADDR(DT_NODELABEL(rom_api))
#define MCHP_XEC_ROM_API_ADDR(n) \
(((uint32_t)(MCHP_XEC_ROM_API_BASE) + ((uint32_t)(n) * 4u)) | BIT(0))
#define MCHP_XEC_ROM_HASH_CREATE_SHA224_ID 95
#define mchp_xec_rom_hash_create_sha224 \
((int (*)(struct mchphash *)) MCHP_XEC_ROM_API_ADDR(MCHP_XEC_ROM_HASH_CREATE_SHA224_ID))
#define MCHP_XEC_ROM_HASH_CREATE_SHA256_ID 96
#define mchp_xec_rom_hash_create_sha256 \
((int (*)(struct mchphash *)) MCHP_XEC_ROM_API_ADDR(MCHP_XEC_ROM_HASH_CREATE_SHA256_ID))
#define MCHP_XEC_ROM_HASH_CREATE_SHA384_ID 97
#define mchp_xec_rom_hash_create_sha384 \
((int (*)(struct mchphash *)) MCHP_XEC_ROM_API_ADDR(MCHP_XEC_ROM_HASH_CREATE_SHA384_ID))
#define MCHP_XEC_ROM_HASH_CREATE_SHA512_ID 98
#define mchp_xec_rom_hash_create_sha512 \
((int (*)(struct mchphash *)) MCHP_XEC_ROM_API_ADDR(MCHP_XEC_ROM_HASH_CREATE_SHA512_ID))
#define MCHP_XEC_ROM_HASH_INIT_STATE_ID 100
#define mec172x_rom_hash_init_state \
((void (*)(struct mchphash *, struct mchphashstate *, char *)) \
MCHP_XEC_ROM_API_ADDR(MCHP_XEC_ROM_HASH_INIT_STATE_ID))
#define MCHP_XEC_ROM_HASH_RESUME_STATE_ID 101
#define mchp_xec_rom_hash_resume_state \
((void (*)(struct mchphash *, struct mchphashstate *)) \
MCHP_XEC_ROM_API_ADDR(MCHP_XEC_ROM_HASH_RESUME_STATE_ID))
#define MCHP_XEC_ROM_HASH_SAVE_STATE_ID 102
#define mchp_xec_rom_hash_save_state \
((int (*)(struct mchphash *)) MCHP_XEC_ROM_API_ADDR(MCHP_XEC_ROM_HASH_SAVE_STATE_ID))
#define MCHP_XEC_ROM_HASH_FEED_ID 103
#define mchp_xec_rom_hash_feed \
((int (*)(struct mchphash *, const uint8_t *, size_t)) \
MCHP_XEC_ROM_API_ADDR(MCHP_XEC_ROM_HASH_FEED_ID))
#define MCHP_XEC_ROM_HASH_DIGEST_ID 104
#define mchp_xec_rom_hash_digest \
((int (*)(struct mchphash *, char *)) MCHP_XEC_ROM_API_ADDR(MCHP_XEC_ROM_HASH_DIGEST_ID))
#define MCHP_XEC_ROM_HASH_WAIT_ID 105
#define mec172x_rom_hash_wait \
((int (*)(struct mchphash *)) MCHP_XEC_ROM_API_ADDR(MCHP_XEC_ROM_HASH_WAIT_ID))
#define MCHP_XEC_ROM_AH_DMA_INIT_ID 144
#define mchp_xec_rom_ah_dma_init \
((int (*)(uint8_t)) MCHP_XEC_ROM_API_ADDR(MCHP_XEC_ROM_AH_DMA_INIT_ID))
#define MCHP_ROM_AH_DMA_INIT_NO_RESET 0
#define MCHP_ROM_AH_DMA_INIT_WITH_RESET 1
#define MCHP_XEC_SYMCR_CAPS_SUPPORT \
(CAP_RAW_KEY | CAP_SEPARATE_IO_BUFS | CAP_SYNC_OPS | CAP_NO_IV_PREFIX)
#define MCHP_XEC_SYMCR_MAX_SESSION 1
#define MCHP_XEC_STATE_BUF_SIZE 256
#define MCHP_XEC_BLOCK_BUF_SIZE 128
struct xec_symcr_hash_session {
struct mchphash mhctx;
struct mchphashstate mhstate;
enum hash_algo algo;
enum mchp_rom_hash_alg_id rom_algo;
bool open;
size_t blksz;
size_t blklen;
uint8_t blockbuf[MCHP_XEC_BLOCK_BUF_SIZE] __aligned(4);
uint8_t statebuf[MCHP_XEC_STATE_BUF_SIZE] __aligned(4);
};
struct xec_symcr_config {
uint32_t regbase;
const struct device *clk_dev;
struct mchp_xec_pcr_clk_ctrl clk_ctrl;
uint8_t irq_num;
uint8_t girq;
uint8_t girq_pos;
uint8_t rsvd1;
};
struct xec_symcr_data {
struct xec_symcr_hash_session hash_sessions[MCHP_XEC_SYMCR_MAX_SESSION];
};
static int mchp_xec_get_unused_session_index(struct xec_symcr_data *data)
{
int i;
for (i = 0; i < MCHP_XEC_SYMCR_MAX_SESSION; i++) {
if (!data->hash_sessions[i].open) {
data->hash_sessions[i].open = true;
return i;
}
}
return -EPERM;
}
struct hash_alg_to_rom {
enum hash_algo algo;
enum mchp_rom_hash_alg_id rom_algo;
};
const struct hash_alg_to_rom hash_alg_tbl[] = {
{ CRYPTO_HASH_ALGO_SHA224, MCHP_ROM_HASH_ALG_SHA224 },
{ CRYPTO_HASH_ALGO_SHA256, MCHP_ROM_HASH_ALG_SHA256 },
{ CRYPTO_HASH_ALGO_SHA384, MCHP_ROM_HASH_ALG_SHA384 },
{ CRYPTO_HASH_ALGO_SHA512, MCHP_ROM_HASH_ALG_SHA512 },
};
static enum mchp_rom_hash_alg_id lookup_hash_alg(enum hash_algo algo)
{
for (size_t n = 0; n < ARRAY_SIZE(hash_alg_tbl); n++) {
if (hash_alg_tbl[n].algo == algo) {
return hash_alg_tbl[n].rom_algo;
}
}
return MCHP_ROM_HASH_ALG_NONE;
}
/* SHA-1, 224, and 256 use block size of 64 bytes
* SHA-384 and 512 use 128 bytes.
*/
static size_t hash_block_size(enum hash_algo algo)
{
switch (algo) {
case CRYPTO_HASH_ALGO_SHA384:
case CRYPTO_HASH_ALGO_SHA512:
return 128u;
default:
return 64u;
}
}
static int init_rom_hash_context(enum mchp_rom_hash_alg_id rom_algo, struct mchphash *c)
{
int ret = 0;
if (!c) {
return -EINVAL;
}
switch (rom_algo) {
case MCHP_ROM_HASH_ALG_SHA224:
ret = mchp_xec_rom_hash_create_sha224(c);
break;
case MCHP_ROM_HASH_ALG_SHA256:
ret = mchp_xec_rom_hash_create_sha256(c);
break;
case MCHP_ROM_HASH_ALG_SHA384:
ret = mchp_xec_rom_hash_create_sha384(c);
break;
case MCHP_ROM_HASH_ALG_SHA512:
ret = mchp_xec_rom_hash_create_sha512(c);
break;
default:
return -EINVAL;
}
if (ret) { /* use zephyr return value */
ret = -EIO;
}
return ret;
}
/* use zephyr return values */
int mchp_xec_rom_hash_init_state_wrapper(struct mchphash *c, struct mchphashstate *h,
uint8_t *dmamem)
{
if (!c || !h || !dmamem) {
return -EINVAL;
}
mec172x_rom_hash_init_state(c, h, (char *)dmamem);
return 0;
}
int mchp_xec_rom_hash_resume_state_wrapper(struct mchphash *c, struct mchphashstate *h)
{
if (!c || !h) {
return -EINVAL;
}
mchp_xec_rom_hash_resume_state(c, h);
return 0;
}
int mchp_xec_rom_hash_save_state_wrapper(struct mchphash *c)
{
if (!c) {
return -EINVAL;
}
if (mchp_xec_rom_hash_save_state(c) != 0) {
return -EIO;
}
return 0;
}
int mchp_xec_rom_hash_feed_wrapper(struct mchphash *c, const uint8_t *msg, size_t sz)
{
if ((!c) || (!msg && sz)) {
return -EINVAL;
}
if (mchp_xec_rom_hash_feed(c, (const char *)msg, sz) != 0) {
return -EIO;
}
return 0;
}
int mchp_xec_rom_hash_digest_wrapper(struct mchphash *c, uint8_t *digest)
{
if (!c || !digest) {
return -EINVAL;
}
if (mchp_xec_rom_hash_digest(c, (char *)digest)) {
return -EIO;
}
return 0;
}
/* Wait for hardware to finish.
* returns 0 if hardware finished with no errors
* returns -EIO if hardware stopped due to error
* returns -EINVAL if parameter is bad, hardware may still be running!
*/
int mchp_xec_rom_hash_wait_wrapper(struct mchphash *c)
{
if (!c) {
return -EINVAL;
}
if (mec172x_rom_hash_wait(c) != 0) {
return -EIO;
}
return 0;
}
/* Called by application for update(finish==false)
* and compute final hash digest(finish==true)
*/
static int xec_symcr_do_hash(struct hash_ctx *ctx, struct hash_pkt *pkt, bool finish)
{
struct xec_symcr_hash_session *hs = NULL;
struct mchphash *c = NULL;
struct mchphashstate *cstate = NULL;
size_t fill_len = 0, rem_len = 0;
int ret = 0;
if (!ctx || !pkt) {
return -EINVAL;
}
hs = (struct xec_symcr_hash_session *)ctx->drv_sessn_state;
c = &hs->mhctx;
cstate = &hs->mhstate;
if (!hs->open) {
LOG_ERR("Session not open");
return -EIO;
}
if (!finish && !pkt->in_len) {
return 0; /* nothing to do */
}
/* Not final digest computation and not enough data to run engine */
if (!finish && ((hs->blklen + pkt->in_len) < hs->blksz)) {
memcpy(&hs->blockbuf[hs->blklen], pkt->in_buf, pkt->in_len);
hs->blklen += pkt->in_len;
return 0;
}
ret = init_rom_hash_context(hs->rom_algo, c);
if (ret) {
LOG_ERR("ROM context init error %d", ret);
return ret;
}
ret = mchp_xec_rom_hash_resume_state_wrapper(c, cstate);
if (ret) {
LOG_ERR("Resume state error %d", ret);
return ret;
}
fill_len = pkt->in_len;
rem_len = 0;
if (!finish) {
rem_len = pkt->in_len & (hs->blksz - 1u);
fill_len = pkt->in_len & ~(hs->blksz - 1u);
if (hs->blklen) {
fill_len = ((hs->blklen + pkt->in_len) & ~(hs->blksz - 1u)) - hs->blklen;
rem_len = pkt->in_len - fill_len;
}
}
if (hs->blklen) {
ret = mchp_xec_rom_hash_feed_wrapper(c, (const uint8_t *)hs->blockbuf, hs->blklen);
if (ret) {
LOG_ERR("ROM hash feed error %d", ret);
return ret;
}
hs->blklen = 0; /* consumed */
}
ret = mchp_xec_rom_hash_feed_wrapper(c, (const uint8_t *)pkt->in_buf, fill_len);
if (ret) {
LOG_ERR("ROM hash feed error %d", ret);
return ret;
}
if (finish) {
ret = mchp_xec_rom_hash_digest_wrapper(c, pkt->out_buf);
if (ret) {
LOG_ERR("ROM Hash final error %d", ret);
return ret;
}
} else {
ret = mchp_xec_rom_hash_save_state(c);
if (ret) {
LOG_ERR("ROM hash save state error %d", ret);
return ret;
}
}
ret = mchp_xec_rom_hash_wait_wrapper(c);
if (ret) {
LOG_ERR("ROM hash wait error %d", ret);
return ret;
}
if (finish) {
hs->blklen = 0;
} else {
memcpy(hs->blockbuf, &pkt->in_buf[fill_len], rem_len);
hs->blklen = rem_len;
}
return 0;
}
static int xec_symcr_hash_session_begin(const struct device *dev, struct hash_ctx *ctx,
enum hash_algo algo)
{
struct xec_symcr_data *data = dev->data;
struct xec_symcr_hash_session *hs = NULL;
struct mchphash *c = NULL;
struct mchphashstate *cstate = NULL;
enum mchp_rom_hash_alg_id rom_algo = MCHP_ROM_HASH_ALG_NONE;
int session_idx = 0;
int ret = 0;
if (ctx->flags & ~(MCHP_XEC_SYMCR_CAPS_SUPPORT)) {
LOG_ERR("Unsupported flag");
return -EINVAL;
}
rom_algo = lookup_hash_alg(algo);
if (rom_algo == MCHP_ROM_HASH_ALG_NONE) {
LOG_ERR("Unsupported algo %d", algo);
return -EINVAL;
}
session_idx = mchp_xec_get_unused_session_index(data);
if (session_idx < 0) {
LOG_ERR("No session available");
return -ENOSPC;
}
hs = &data->hash_sessions[session_idx];
hs->algo = algo;
hs->rom_algo = rom_algo;
hs->open = false;
hs->blklen = 0;
hs->blksz = hash_block_size(algo);
ctx->drv_sessn_state = hs;
ctx->started = false;
ctx->hash_hndlr = xec_symcr_do_hash;
/* reset HW at beginning of session */
ret = mchp_xec_rom_ah_dma_init(MCHP_ROM_AH_DMA_INIT_WITH_RESET);
if (ret) {
LOG_ERR("ROM HW init error %d", ret);
return -EIO;
}
c = &hs->mhctx;
cstate = &hs->mhstate;
ret = init_rom_hash_context(hs->rom_algo, c);
if (ret) {
LOG_ERR("ROM HW context init error %d", ret);
return ret;
}
ret = mchp_xec_rom_hash_init_state_wrapper(c, cstate, hs->statebuf);
if (ret) {
LOG_ERR("ROM HW init state error %d", ret);
}
hs->open = true;
return ret;
}
/*
* struct hash_ctx {
* const struct device *device; this device driver's instance structure
* void *drv_sessn_state; pointer to driver instance struct session state. Defined by driver
* hash_op hash_hndlr; pointer to this driver function. App calls via pointer to do operations
* bool started; true if multipart hash has been started
* uint16_t flags; app populates this before calling hash_begin_session
* }
*/
static int xec_symcr_hash_session_free(const struct device *dev, struct hash_ctx *ctx)
{
struct xec_symcr_hash_session *hs = NULL;
int ret = 0;
ret = mchp_xec_rom_ah_dma_init(MCHP_ROM_AH_DMA_INIT_WITH_RESET);
if (ret) {
ret = -EIO;
LOG_ERR("ROM HW reset error %d", ret);
}
hs = (struct xec_symcr_hash_session *)ctx->drv_sessn_state;
memset(hs, 0, sizeof(struct xec_symcr_hash_session));
return ret;
}
static int xec_symcr_query_hw_caps(const struct device *dev)
{
return MCHP_XEC_SYMCR_CAPS_SUPPORT;
}
static int xec_symcr_init(const struct device *dev)
{
const struct xec_symcr_config *cfg = dev->config;
int ret;
if (!device_is_ready(cfg->clk_dev)) {
LOG_ERR("clock device not ready");
return -ENODEV;
}
ret = clock_control_on(cfg->clk_dev, (clock_control_subsys_t *)&cfg->clk_ctrl);
if (ret < 0) {
LOG_ERR("clock on error %d", ret);
return ret;
}
ret = mchp_xec_rom_ah_dma_init(MCHP_ROM_AH_DMA_INIT_WITH_RESET);
if (ret) {
ret = -EIO;
}
return ret;
}
static struct crypto_driver_api xec_symcr_api = {
.query_hw_caps = xec_symcr_query_hw_caps,
.hash_begin_session = xec_symcr_hash_session_begin,
.hash_free_session = xec_symcr_hash_session_free,
};
#define XEC_SYMCR_PCR_INFO(i) \
MCHP_XEC_PCR_SCR_ENCODE(DT_INST_CLOCKS_CELL(i, regidx), \
DT_INST_CLOCKS_CELL(i, bitpos), \
DT_INST_CLOCKS_CELL(i, domain))
#define XEC_SYMCR_INIT(inst) \
\
static struct xec_symcr_data xec_symcr_data_##inst; \
\
static const struct xec_symcr_config xec_symcr_cfg_##inst = { \
.regbase = DT_INST_REG_ADDR(inst), \
.clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(inst)), \
.clk_ctrl = { \
.pcr_info = XEC_SYMCR_PCR_INFO(inst), \
}, \
.irq_num = DT_INST_IRQN(inst), \
.girq = DT_INST_PROP_BY_IDX(inst, girqs, 0), \
.girq_pos = DT_INST_PROP_BY_IDX(inst, girqs, 1), \
}; \
\
DEVICE_DT_INST_DEFINE(inst, &xec_symcr_init, NULL, \
&xec_symcr_data_##inst, &xec_symcr_cfg_##inst, \
POST_KERNEL, CONFIG_CRYPTO_INIT_PRIORITY, \
&xec_symcr_api);
DT_INST_FOREACH_STATUS_OKAY(XEC_SYMCR_INIT)