/* * Copyright (c) 2022 Nuvoton Technology Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nuvoton_npcx_sha #include #include #include #include LOG_MODULE_REGISTER(sha_npcx, CONFIG_CRYPTO_LOG_LEVEL); #include "soc_ncl.h" #define NPCX_HASH_CAPS_SUPPORT (CAP_SEPARATE_IO_BUFS | CAP_SYNC_OPS) #define NPCX_SHA256_HANDLE_SIZE DT_INST_PROP(0, context_buffer_size) #define NPCX_SHA_MAX_SESSION 1 /* The following table holds the function pointer for each SHA API in NPCX ROM. */ struct npcx_ncl_sha { /* Get the SHA context size required by SHA APIs. */ uint32_t (*get_context_size)(void); /* Initial SHA context. */ enum ncl_status (*init_context)(void *ctx); /* Finalize SHA context. */ enum ncl_status (*finalize_context)(void *ctx); /* Initiate the SHA hardware module and setups needed parameters. */ enum ncl_status (*init)(void *ctx); /* * Prepare the context buffer for a SHA calculation - by loading the * initial SHA-256/384/512 parameters. */ enum ncl_status (*start)(void *ctx, enum ncl_sha_type type); /* * Updates the SHA calculation with the additional data. When the * function returns, the hardware and memory buffer shall be ready to * accept new data * buffers for SHA calculation and changes to the data * in data buffer should no longer effect the SHA calculation. */ enum ncl_status (*update)(void *ctx, const uint8_t *data, uint32_t Len); /* Return the SHA result (digest.) */ enum ncl_status (*finish)(void *ctx, uint8_t *hashDigest); /* Perform a complete SHA calculation */ enum ncl_status (*calc)(void *ctx, enum ncl_sha_type type, const uint8_t *data, uint32_t Len, uint8_t *hashDigest); /* Power on/off the SHA module. */ enum ncl_status (*power)(void *ctx, uint8_t enable); /* Reset the SHA hardware and terminate any in-progress operations. */ enum ncl_status (*reset)(void *ctx); }; /* The start address of the SHA API table. */ #define NPCX_NCL_SHA ((const struct npcx_ncl_sha *)DT_INST_REG_ADDR(0)) struct npcx_sha_context { uint8_t handle[NPCX_SHA256_HANDLE_SIZE]; } __aligned(4); struct npcx_sha_session { struct npcx_sha_context npcx_sha_ctx; enum hash_algo algo; bool in_use; }; struct npcx_sha_session npcx_sessions[NPCX_SHA_MAX_SESSION]; static int npcx_get_unused_session_index(void) { int i; for (i = 0; i < NPCX_SHA_MAX_SESSION; i++) { if (!npcx_sessions[i].in_use) { npcx_sessions[i].in_use = true; return i; } } return -1; } static int npcx_sha_compute(struct hash_ctx *ctx, struct hash_pkt *pkt, bool finish) { enum ncl_status ret; struct npcx_sha_session *npcx_session = ctx->drv_sessn_state; struct npcx_sha_context *npcx_ctx = &npcx_session->npcx_sha_ctx; enum ncl_sha_type sha_type; switch (npcx_session->algo) { case CRYPTO_HASH_ALGO_SHA256: sha_type = NCL_SHA_TYPE_2_256; break; case CRYPTO_HASH_ALGO_SHA384: sha_type = NCL_SHA_TYPE_2_384; break; case CRYPTO_HASH_ALGO_SHA512: sha_type = NCL_SHA_TYPE_2_512; break; default: LOG_ERR("Unexpected algo: %d", npcx_session->algo); return -EINVAL; } if (!ctx->started) { ret = NPCX_NCL_SHA->start(npcx_ctx->handle, sha_type); if (ret != NCL_STATUS_OK) { LOG_ERR("Could not compute the hash, err:%d", ret); return -EINVAL; } ctx->started = true; } if (pkt->in_len != 0) { ret = NPCX_NCL_SHA->update(npcx_ctx->handle, pkt->in_buf, pkt->in_len); if (ret != NCL_STATUS_OK) { LOG_ERR("Could not update the hash, err:%d", ret); ctx->started = false; return -EINVAL; } } if (finish) { ctx->started = false; ret = NPCX_NCL_SHA->finish(npcx_ctx->handle, pkt->out_buf); if (ret != NCL_STATUS_OK) { LOG_ERR("Could not compute the hash, err:%d", ret); return -EINVAL; } } return 0; } static int npcx_hash_session_setup(const struct device *dev, struct hash_ctx *ctx, enum hash_algo algo) { int ctx_idx; struct npcx_sha_context *npcx_ctx; if (ctx->flags & ~(NPCX_HASH_CAPS_SUPPORT)) { LOG_ERR("Unsupported flag"); return -EINVAL; } if ((algo != CRYPTO_HASH_ALGO_SHA256) && (algo != CRYPTO_HASH_ALGO_SHA384) && (algo != CRYPTO_HASH_ALGO_SHA512)) { LOG_ERR("Unsupported algo: %d", algo); return -EINVAL; } ctx_idx = npcx_get_unused_session_index(); if (ctx_idx < 0) { LOG_ERR("No free session for now"); return -ENOSPC; } npcx_sessions[ctx_idx].algo = algo; ctx->drv_sessn_state = &npcx_sessions[ctx_idx]; ctx->started = false; ctx->hash_hndlr = npcx_sha_compute; npcx_ctx = &npcx_sessions[ctx_idx].npcx_sha_ctx; NPCX_NCL_SHA->init_context(npcx_ctx->handle); NPCX_NCL_SHA->power(npcx_ctx->handle, 1); NPCX_NCL_SHA->init(npcx_ctx->handle); NPCX_NCL_SHA->reset(npcx_ctx->handle); return 0; } static int npcx_hash_session_free(const struct device *dev, struct hash_ctx *ctx) { struct npcx_sha_session *npcx_session = ctx->drv_sessn_state; struct npcx_sha_context *npcx_ctx = &npcx_session->npcx_sha_ctx; NPCX_NCL_SHA->reset(npcx_ctx->handle); NPCX_NCL_SHA->power(npcx_ctx->handle, 0); NPCX_NCL_SHA->finalize_context(npcx_ctx->handle); npcx_session->in_use = false; return 0; } static int npcx_query_caps(const struct device *dev) { return NPCX_HASH_CAPS_SUPPORT; } static int npcx_hash_init(const struct device *dev) { uint32_t handle_size_required; handle_size_required = NPCX_NCL_SHA->get_context_size(); if (handle_size_required != NPCX_SHA256_HANDLE_SIZE) { LOG_ERR("Pre-alloc buf size doesn't match required buf size (%d)", handle_size_required); return -ENOSR; } return 0; } static const struct crypto_driver_api npcx_crypto_api = { .hash_begin_session = npcx_hash_session_setup, .hash_free_session = npcx_hash_session_free, .query_hw_caps = npcx_query_caps, }; DEVICE_DT_INST_DEFINE(0, npcx_hash_init, NULL, NULL, NULL, POST_KERNEL, CONFIG_CRYPTO_INIT_PRIORITY, &npcx_crypto_api); BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, "only one 'nuvoton,npcx-sha' compatible node can be supported");