1 /*
2  * Copyright (c) 2022 Nuvoton Technology Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nuvoton_npcx_sha
8 
9 #include <errno.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/crypto/crypto.h>
12 
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(sha_npcx, CONFIG_CRYPTO_LOG_LEVEL);
15 
16 #include "soc_ncl.h"
17 
18 #define NPCX_HASH_CAPS_SUPPORT  (CAP_SEPARATE_IO_BUFS | CAP_SYNC_OPS)
19 #define NPCX_SHA256_HANDLE_SIZE DT_INST_PROP(0, context_buffer_size)
20 #define NPCX_SHA_MAX_SESSION    1
21 
22 /* The following table holds the function pointer for each SHA API in NPCX ROM. */
23 struct npcx_ncl_sha {
24 	/* Get the SHA context size required by SHA APIs. */
25 	uint32_t (*get_context_size)(void);
26 	/* Initial SHA context. */
27 	enum ncl_status (*init_context)(void *ctx);
28 	/* Finalize SHA context. */
29 	enum ncl_status (*finalize_context)(void *ctx);
30 	/* Initiate the SHA hardware module and setups needed parameters. */
31 	enum ncl_status (*init)(void *ctx);
32 	/*
33 	 * Prepare the context buffer for a SHA calculation -  by loading the
34 	 * initial SHA-256/384/512 parameters.
35 	 */
36 	enum ncl_status (*start)(void *ctx, enum ncl_sha_type type);
37 	/*
38 	 * Updates the SHA calculation with the additional data. When the
39 	 * function returns, the hardware and memory buffer shall be ready to
40 	 * accept new data * buffers for SHA calculation and changes to the data
41 	 * in data buffer should no longer effect the SHA calculation.
42 	 */
43 	enum ncl_status (*update)(void *ctx, const uint8_t *data, uint32_t Len);
44 	/* Return the SHA result (digest.) */
45 	enum ncl_status (*finish)(void *ctx, uint8_t *hashDigest);
46 	/* Perform a complete SHA calculation */
47 	enum ncl_status (*calc)(void *ctx, enum ncl_sha_type type, const uint8_t *data,
48 				uint32_t Len, uint8_t *hashDigest);
49 	/* Power on/off the SHA module. */
50 	enum ncl_status (*power)(void *ctx, uint8_t enable);
51 	/* Reset the SHA hardware and terminate any in-progress operations. */
52 	enum ncl_status (*reset)(void *ctx);
53 };
54 
55 /* The start address of the SHA API table. */
56 #define NPCX_NCL_SHA ((const struct npcx_ncl_sha *)DT_INST_REG_ADDR(0))
57 
58 struct npcx_sha_context {
59 	uint8_t handle[NPCX_SHA256_HANDLE_SIZE];
60 } __aligned(4);
61 
62 struct npcx_sha_session {
63 	struct npcx_sha_context npcx_sha_ctx;
64 	enum hash_algo algo;
65 	bool in_use;
66 };
67 
68 struct npcx_sha_session npcx_sessions[NPCX_SHA_MAX_SESSION];
69 
npcx_get_unused_session_index(void)70 static int npcx_get_unused_session_index(void)
71 {
72 	int i;
73 
74 	for (i = 0; i < NPCX_SHA_MAX_SESSION; i++) {
75 		if (!npcx_sessions[i].in_use) {
76 			npcx_sessions[i].in_use = true;
77 			return i;
78 		}
79 	}
80 
81 	return -1;
82 }
npcx_sha_compute(struct hash_ctx * ctx,struct hash_pkt * pkt,bool finish)83 static int npcx_sha_compute(struct hash_ctx *ctx, struct hash_pkt *pkt, bool finish)
84 {
85 	enum ncl_status ret;
86 	struct npcx_sha_session *npcx_session = ctx->drv_sessn_state;
87 	struct npcx_sha_context *npcx_ctx = &npcx_session->npcx_sha_ctx;
88 	enum ncl_sha_type sha_type;
89 
90 	switch (npcx_session->algo) {
91 	case CRYPTO_HASH_ALGO_SHA256:
92 		sha_type = NCL_SHA_TYPE_2_256;
93 		break;
94 	case CRYPTO_HASH_ALGO_SHA384:
95 		sha_type = NCL_SHA_TYPE_2_384;
96 		break;
97 	case CRYPTO_HASH_ALGO_SHA512:
98 		sha_type = NCL_SHA_TYPE_2_512;
99 		break;
100 	default:
101 		LOG_ERR("Unexpected algo: %d", npcx_session->algo);
102 		return -EINVAL;
103 	}
104 
105 	if (!ctx->started) {
106 		ret = NPCX_NCL_SHA->start(npcx_ctx->handle, sha_type);
107 		if (ret != NCL_STATUS_OK) {
108 			LOG_ERR("Could not compute the hash, err:%d", ret);
109 			return -EINVAL;
110 		}
111 		ctx->started = true;
112 	}
113 
114 	if (pkt->in_len != 0) {
115 		ret = NPCX_NCL_SHA->update(npcx_ctx->handle, pkt->in_buf, pkt->in_len);
116 		if (ret != NCL_STATUS_OK) {
117 			LOG_ERR("Could not update the hash, err:%d", ret);
118 			ctx->started = false;
119 			return -EINVAL;
120 		}
121 	}
122 
123 	if (finish) {
124 		ctx->started = false;
125 		ret = NPCX_NCL_SHA->finish(npcx_ctx->handle, pkt->out_buf);
126 		if (ret != NCL_STATUS_OK) {
127 			LOG_ERR("Could not compute the hash, err:%d", ret);
128 			return -EINVAL;
129 		}
130 	}
131 
132 	return 0;
133 }
134 
npcx_hash_session_setup(const struct device * dev,struct hash_ctx * ctx,enum hash_algo algo)135 static int npcx_hash_session_setup(const struct device *dev, struct hash_ctx *ctx,
136 				   enum hash_algo algo)
137 {
138 	int ctx_idx;
139 	struct npcx_sha_context *npcx_ctx;
140 
141 	if (ctx->flags & ~(NPCX_HASH_CAPS_SUPPORT)) {
142 		LOG_ERR("Unsupported flag");
143 		return -EINVAL;
144 	}
145 
146 	if ((algo != CRYPTO_HASH_ALGO_SHA256) && (algo != CRYPTO_HASH_ALGO_SHA384) &&
147 	    (algo != CRYPTO_HASH_ALGO_SHA512)) {
148 		LOG_ERR("Unsupported algo: %d", algo);
149 		return -EINVAL;
150 	}
151 
152 	ctx_idx = npcx_get_unused_session_index();
153 	if (ctx_idx < 0) {
154 		LOG_ERR("No free session for now");
155 		return -ENOSPC;
156 	}
157 
158 	npcx_sessions[ctx_idx].algo = algo;
159 
160 	ctx->drv_sessn_state = &npcx_sessions[ctx_idx];
161 	ctx->started = false;
162 	ctx->hash_hndlr = npcx_sha_compute;
163 
164 	npcx_ctx = &npcx_sessions[ctx_idx].npcx_sha_ctx;
165 	NPCX_NCL_SHA->init_context(npcx_ctx->handle);
166 	NPCX_NCL_SHA->power(npcx_ctx->handle, 1);
167 	NPCX_NCL_SHA->init(npcx_ctx->handle);
168 	NPCX_NCL_SHA->reset(npcx_ctx->handle);
169 
170 	return 0;
171 }
172 
npcx_hash_session_free(const struct device * dev,struct hash_ctx * ctx)173 static int npcx_hash_session_free(const struct device *dev, struct hash_ctx *ctx)
174 {
175 	struct npcx_sha_session *npcx_session = ctx->drv_sessn_state;
176 	struct npcx_sha_context *npcx_ctx = &npcx_session->npcx_sha_ctx;
177 
178 	NPCX_NCL_SHA->reset(npcx_ctx->handle);
179 	NPCX_NCL_SHA->power(npcx_ctx->handle, 0);
180 	NPCX_NCL_SHA->finalize_context(npcx_ctx->handle);
181 	npcx_session->in_use = false;
182 
183 	return 0;
184 }
185 
npcx_query_caps(const struct device * dev)186 static int npcx_query_caps(const struct device *dev)
187 {
188 	return NPCX_HASH_CAPS_SUPPORT;
189 }
190 
npcx_hash_init(const struct device * dev)191 static int npcx_hash_init(const struct device *dev)
192 {
193 	uint32_t handle_size_required;
194 
195 	handle_size_required = NPCX_NCL_SHA->get_context_size();
196 	if (handle_size_required != NPCX_SHA256_HANDLE_SIZE) {
197 		LOG_ERR("Pre-alloc buf size doesn't match required buf size (%d)",
198 			handle_size_required);
199 		return -ENOSR;
200 	}
201 
202 	return 0;
203 }
204 
205 static DEVICE_API(crypto, npcx_crypto_api) = {
206 	.hash_begin_session = npcx_hash_session_setup,
207 	.hash_free_session = npcx_hash_session_free,
208 	.query_hw_caps = npcx_query_caps,
209 };
210 
211 DEVICE_DT_INST_DEFINE(0, npcx_hash_init, NULL, NULL, NULL, POST_KERNEL, CONFIG_CRYPTO_INIT_PRIORITY,
212 		      &npcx_crypto_api);
213 BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1,
214 	     "only one 'nuvoton,npcx-sha' compatible node can be supported");
215