/* * Copyright (c) 2022 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include "crypto_intel_sha_priv.h" #include LOG_MODULE_REGISTER(SHA); #define DT_DRV_COMPAT intel_sha static struct sha_session sha_sessions[SHA_MAX_SESSIONS]; static int intel_sha_get_unused_session_idx(void) { int i; for (i = 0; i < SHA_MAX_SESSIONS; i++) { if (!sha_sessions[i].in_use) { sha_sessions[i].in_use = true; return i; } } return -1; } static int intel_sha_set_ctl_enable(struct sha_container *sha, int status) { /* wait until not busy when turning off */ if (status == 0 && sha->dfsha->shactl.part.en == 1) { while (sha->dfsha->shasts.part.busy) { } } sha->dfsha->shactl.part.en = status; return 0; } static int intel_sha_set_resume_length_dw0(struct sha_container *sha, uint32_t lower_length) { int err = -EINVAL; if (IS_ALIGNED(lower_length, SHA_REQUIRED_BLOCK_ALIGNMENT)) { sha->dfsha->sharldw0.full = lower_length; err = 0; } return err; } static int intel_sha_set_resume_length_dw1(struct sha_container *sha, uint32_t upper_length) { sha->dfsha->sharldw1.full = upper_length; return 0; } static int intel_sha_regs_cpy(void *dst, const void *src, size_t len) { uint32_t counter; int err = -EINVAL; if ((IS_ALIGNED(len, sizeof(uint32_t))) && (IS_ALIGNED(dst, sizeof(uint32_t))) && (IS_ALIGNED(src, sizeof(uint32_t)))) { len /= sizeof(uint32_t); for (counter = 0; counter != len; ++counter) { ((uint32_t *)dst)[counter] = ((uint32_t *)src)[counter]; } err = 0; } return err; } /* ! Perform SHA computation over requested region. */ static int intel_sha_device_run(struct device *dev, const void *buf_in, size_t buf_in_size, size_t max_buff_len, uint32_t state) { int err; struct sha_container *const self = dev->data; union sha_state state_u = { .full = state }; /* align to OWORD */ const size_t aligned_buff_size = ROUND_UP(buf_in_size, 0x10); err = intel_sha_set_ctl_enable(self, 0); if (err) { return err; } /* set processing element disable */ self->dfsha->pibcs.part.peen = 0; /* set pib base addr */ self->dfsha->pibba.full = (uint32_t)buf_in; if (max_buff_len < aligned_buff_size) { return -EINVAL; } self->dfsha->pibs.full = aligned_buff_size; /* enable interrupt */ self->dfsha->pibcs.part.bscie = 1; self->dfsha->pibcs.part.teie = 0; /* set processing element enable */ self->dfsha->pibcs.part.peen = 1; if (self->dfsha->shactl.part.en) { return -EINVAL; /* already enabled */ } self->dfsha->shactl.part.hrsm = state_u.part.hrsm; /* set initial values if resuming */ if (state_u.part.hrsm) { err = intel_sha_set_resume_length_dw0(self, self->dfsha->shaaldw0.full); if (err) { return err; } err = intel_sha_set_resume_length_dw1(self, self->dfsha->shaaldw1.full); if (err) { return err; } err = intel_sha_regs_cpy((void *)self->dfsha->initial_vector, (void *)self->dfsha->sha_result, sizeof(self->dfsha->initial_vector)); if (err) { return err; } } /* set ctl hash first middle */ if (self->dfsha->shactl.part.en) { return -EINVAL; /* already enabled */ } self->dfsha->shactl.part.hfm = state_u.part.state; /* increment pointer */ self->dfsha->pibfpi.full = buf_in_size; err = intel_sha_set_ctl_enable(self, 1); if (err) { return err; } err = intel_sha_set_ctl_enable(self, 0); return err; } static int intel_sha_copy_hash(struct sha_container *const self, void *dst, size_t len) { /* NOTE: generated hash value should be read from the end */ int err = -EINVAL; uint32_t counter = 0; uint32_t last_idx = 0; if ((IS_ALIGNED(len, sizeof(uint32_t))) && (IS_ALIGNED(dst, sizeof(uint32_t)))) { len /= sizeof(uint32_t); counter = 0; /* The index of a last element in the sha result buffer. */ last_idx = (sizeof(self->dfsha->sha_result) / sizeof(uint32_t)) - 1; for (counter = 0; counter != len; counter++) { ((uint32_t *)dst)[counter] = ((uint32_t *)self->dfsha->sha_result)[last_idx - counter]; } err = 0; } return err; } static int intel_sha_device_get_hash(struct device *dev, void *buf_out, size_t buf_out_size) { int err; struct sha_container *const self = dev->data; if (buf_out == NULL) { return -EINVAL; } /* wait until not busy */ while (self->dfsha->shasts.part.busy) { } err = intel_sha_copy_hash(self, buf_out, buf_out_size); return err; } static int intel_sha_compute(struct hash_ctx *ctx, struct hash_pkt *pkt, bool finish) { int ret; struct sha_container *self = (struct sha_container *const)(ctx->device)->data; struct sha_session *session = (struct sha_session *)ctx->drv_sessn_state; size_t frag_length; size_t output_size; uint32_t *hash_int_ptr = (uint32_t *)(pkt->out_buf); /* set algo */ self->dfsha->shactl.full = 0x0; self->dfsha->shactl.part.algo = session->algo; /* restore ctx */ self->dfsha->shaaldw0 = session->sha_ctx.shaaldw0; self->dfsha->shaaldw1 = session->sha_ctx.shaaldw1; ret = intel_sha_regs_cpy((void *)self->dfsha->initial_vector, (void *)session->sha_ctx.initial_vector, sizeof(self->dfsha->initial_vector)); if (ret) { return ret; } ret = intel_sha_regs_cpy((void *)self->dfsha->sha_result, (void *)session->sha_ctx.sha_result, sizeof(self->dfsha->sha_result)); if (ret) { return ret; } /* compute hash */ do { frag_length = pkt->in_len > SHA_API_MAX_FRAG_LEN ? SHA_API_MAX_FRAG_LEN : pkt->in_len; if ((frag_length == pkt->in_len) && finish) { session->state.part.state = SHA_LAST; } ret = intel_sha_device_run(ctx->device, pkt->in_buf, frag_length, frag_length, session->state.full); if (ret) { return ret; } /* set state for next iteration */ session->state.part.hrsm = SHA_HRSM_ENABLE; session->state.part.state = SHA_MIDLE; pkt->in_len -= frag_length; pkt->in_buf += frag_length; } while (pkt->in_len > 0); if (finish) { switch (self->dfsha->shactl.part.algo) { case CRYPTO_HASH_ALGO_SHA224: output_size = SHA224_ALGORITHM_HASH_SIZEOF; break; case CRYPTO_HASH_ALGO_SHA256: output_size = SHA256_ALGORITHM_HASH_SIZEOF; break; case CRYPTO_HASH_ALGO_SHA384: output_size = SHA384_ALGORITHM_HASH_SIZEOF; break; case CRYPTO_HASH_ALGO_SHA512: output_size = SHA512_ALGORITHM_HASH_SIZEOF; break; default: return -ENOTSUP; } ret = intel_sha_device_get_hash(ctx->device, pkt->out_buf, output_size); if (ret) { return ret; } /* Fix byte ordering to match common hash representation. */ for (size_t i = 0; i != output_size / sizeof(uint32_t); i++) { hash_int_ptr[i] = BYTE_SWAP32(hash_int_ptr[i]); } } return ret; } static int intel_sha_device_set_hash_type(struct device *dev, struct hash_ctx *ctx, enum hash_algo algo) { int ret; int ctx_idx; struct sha_container *self = (struct sha_container *const)(dev)->data; ctx_idx = intel_sha_get_unused_session_idx(); if (ctx_idx < 0) { LOG_ERR("All sessions in use!"); return -ENOSPC; } ctx->drv_sessn_state = &sha_sessions[ctx_idx]; /* set processing element enable */ self->dfsha->pibcs.part.peen = 0; /* populate sha session data */ sha_sessions[ctx_idx].state.part.state = SHA_FIRST; sha_sessions[ctx_idx].state.part.hrsm = SHA_HRSM_DISABLE; sha_sessions[ctx_idx].algo = algo; ctx->hash_hndlr = intel_sha_compute; return ret; } static int intel_sha_device_free(const struct device *dev, struct hash_ctx *ctx) { struct sha_container *self = (struct sha_container *const)(dev)->data; struct sha_session *session = (struct sha_session *)ctx->drv_sessn_state; (void)memset(self->dfsha, 0, sizeof(struct sha_hw_regs)); (void)memset(&session->sha_ctx, 0, sizeof(struct sha_context)); (void)memset(&session->state, 0, sizeof(union sha_state)); session->in_use = 0; session->algo = 0; return 0; } static int intel_sha_device_init(struct device *dev) { return 0; } static int intel_sha_device_hw_caps(const struct device *dev) { return (CAP_SEPARATE_IO_BUFS | CAP_SYNC_OPS); } static struct sha_container sha_data = { .dfsha = DT_INST_REG_ADDR_BY_IDX(0, 0) }; static struct crypto_driver_api hash_enc_funcs = { .hash_begin_session = intel_sha_device_set_hash_type, .hash_free_session = intel_sha_device_free, .hash_async_callback_set = NULL, .query_hw_caps = intel_sha_device_hw_caps, }; DEVICE_DT_INST_DEFINE(0, intel_sha_device_init, NULL, &sha_data, NULL, POST_KERNEL, CONFIG_CRYPTO_INIT_PRIORITY, (void *)&hash_enc_funcs);