549 lines
13 KiB
C
549 lines
13 KiB
C
/*
|
|
* 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 const 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)
|