| /* |
| * 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) |