You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
548 lines
13 KiB
548 lines
13 KiB
/* |
|
* 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 DEVICE_API(crypto, 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)
|
|
|