blob: 9ee05b44ef1226971f693e43f7479ee90cfc3042 [file] [log] [blame]
/*
* Copyright (c) 2022 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/init.h>
#include <errno.h>
#include <zephyr/crypto/crypto.h>
#include "crypto_intel_sha_priv.h"
#include <zephyr/logging/log.h>
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);