/* * Copyright (c) 2023 Microchip Technology Inc. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT microchip_xec_symcr #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(xec_symcr, CONFIG_CRYPTO_LOG_LEVEL); #include /* 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)