1 /*
2  *  Copyright (C) 2017-2024 Alibaba Group Holding Limited
3  */
4 
5 /*******************************************************
6  * @file    wj_sha.c
7  * @brief   source file for sha csi driver
8  * @version V2.0
9  * @date    14. Sept 2020
10  * ******************************************************/
11 
12 #include <drv/sha.h>
13 #include <drv/irq.h>
14 
15 /**
16  * Different modes get different plaintext blocks
17  */
18 #define SHA_GET_BOLOCK_SIZE_BYTES(_mod_)      (((_mod_) < SHA_MODE_512) ? (uint32_t)64 : (uint32_t)128)
19 
20 /**
21  * Different modes get different data lengths
22  * \note SHA-1\SHA-224\SHA-256 need 2 * 4 = 8 bytes
23  *       SHA-512\SHA-384       need 4 * 4 = 16 bytes
24  */
25 #define SHA_GET_MSGLEN_TAIL_4BYTES(_mod_)    (((_mod_) < SHA_MODE_512) ? (uint32_t)2 : (uint32_t)4)
26 
27 /**
28  * Number of result message digest bytes retrieved by sha mode
29  */
30 #define SHA_GET_MSGDIGEST_BYTES(_mod_)       (((_mod_) == SHA_MODE_1  ) ? (uint32_t)20 :    \
31         ((_mod_) == SHA_MODE_256) ? (uint32_t)32 :    \
32         ((_mod_) == SHA_MODE_224) ? (uint32_t)28 :    \
33         ((_mod_) == SHA_MODE_512) ? (uint32_t)64 :    \
34         ((_mod_) == SHA_MODE_384) ? (uint32_t)48 :    \
35         (uint32_t)20)
36 
37 #define SHA_ALG_ALIGN_SORT(_idx_)            (_idx_ % 2U ? ((_idx_ - 1U) >> 1) : ((_idx_ >> 1) + 8U))
38 
39 #define SHA_ALG_SWAP(_x_, _y_)               {   \
40         uint8_t z = (uint8_t)*_x_;                  \
41         *_x_ = (uint8_t)*_y_;                       \
42         *_y_ = z;                                   \
43     }
44 
45 #define SHA_ALG_ALIGN_4BYTE(_len_)           (((_len_) % 4U) ? ((((_len_) >> 2U) << 2U) + 4U) : (_len_))
46 
47 
48 #define SHA_ALG_SELECT_MIN(_x_, _y_)         (((_x_) < (_y_)) ? (_x_) : (_y_))
49 
50 #define SHA_WAIT_WRITED_10S                                                        (10000U)
51 
52 #define SHA_WAIT_IS_TIMEOUT(_time_ms_, _result_)                                   {        \
53         do {                                                                                   \
54             if (_time_ms_ >= SHA_WAIT_WRITED_10S) {                                         \
55                 _result_ = -1;                                                                 \
56             }                                                                                  \
57         } while(0);                                                                            \
58     }
59 
60 /*<! Private function documentation */
61 
62 /**
63   \brief       SHA data sort
64   \param[in]   d_addr       operate data dest
65   \param[in]   s_addr       operate data source
66   \param[in]   idx          current data idx
67   \return      null
68 */
wj_sha_alg_sort(uint8_t * d_addr,uint8_t * s_addr,uint8_t idx)69 void wj_sha_alg_sort(uint8_t *d_addr, uint8_t *s_addr, uint8_t idx)
70 {
71     *(d_addr + (idx << 2) + 0U) = *(s_addr + 0U);
72     *(d_addr + (idx << 2) + 1U) = *(s_addr + 1U);
73     *(d_addr + (idx << 2) + 2U) = *(s_addr + 2U);
74     *(d_addr + (idx << 2) + 3U) = *(s_addr + 3U);
75 }
76 
77 /**
78   \brief       SHA data flipping
79   \param[in]   p       operate start address
80   \param[in]   n       operate data length
81   \return      null
82 */
wj_sha_alg_reverse(uint8_t * p,uint8_t n)83 void wj_sha_alg_reverse(uint8_t *p, uint8_t n)
84 {
85     if (n) {
86         SHA_ALG_SWAP((p + 0U), (p + 3U));        ///< BYTE0 <<---SWAP--->> BYTE3
87         SHA_ALG_SWAP((p + 1U), (p + 2U));        ///< BYTE1 <<---SWAP--->> BYTE2
88 
89         wj_sha_alg_reverse((p + 4U), (n - 1U));     ///< offset next address(uint32_t) and reduce operate data count
90     }
91 }
92 
93 /**
94   \brief       SHA interrupt handling function
95   \param[in]   arg        Callback function member variables
96   \return      null
97 */
wj_sha_irq_handler(void * arg)98 void wj_sha_irq_handler(void *arg)
99 {
100     csi_sha_t *sha = (csi_sha_t *)arg;
101     uint32_t sha_irq_state = 0U;
102     ///< TODO:获取中断状态到sha_irq_state
103     if (sha_irq_state) {
104         if (sha->callback) {
105             sha->callback(sha, SHA_EVENT_COMPLETE, sha->arg);
106         }
107 
108         ///< TODO:清除SHA 中断状态
109     }
110 }
111 
112 /**
113   \brief       update the engine
114   \param[in]   sha     sha handle to operate.
115   \param[in]   context Pointer to the sha context
116   \param[in]   input   Pointer to the Source data
117   \param[in]   size    the data size
118   \return      \ref csi_error_t
119 */
wj_sha_update_sub(csi_sha_t * sha,csi_sha_context_t * context,const void * input,uint32_t size)120 csi_error_t wj_sha_update_sub(csi_sha_t *sha, csi_sha_context_t *context, const void *input, uint32_t size)
121 {
122     CSI_PARAM_CHK(sha, CSI_ERROR);
123     CSI_PARAM_CHK(context, CSI_ERROR);
124     csi_error_t ret = CSI_OK;
125 
126     uint8_t *p_data_in = (uint8_t *)input;
127 
128     sha->state.busy = 1U;
129 
130     *(p_data_in + size) = 0x80U;
131 
132     ///< TODO:写入计算值
133 
134     ///< TODO:开始SHA计算
135 
136     mdelay(1U);                                          ///<! Must be kept, otherwise the data is prone to error
137 
138     sha->state.busy = 0U;
139 
140     return ret;
141 }
142 
143 /*<! Public function documentation */
144 
145 /**
146   \brief       Initialize SHA Interface. 1. Initializes the resources needed for the SHA interface 2.registers event callback function
147   \param[in]   sha  operate handle.
148   \param[in]   idx index of sha
149   \return      \ref csi_error_t
150 */
csi_sha_init(csi_sha_t * sha,uint32_t idx)151 csi_error_t csi_sha_init(csi_sha_t *sha, uint32_t idx)
152 {
153     CSI_PARAM_CHK(sha, CSI_ERROR);
154     csi_error_t ret = CSI_OK;
155 
156     ///< 获取中断号、基地址等相关信息
157     if (0 == target_get(DEV_WJ_SHA_TAG, idx, &sha->dev)) {
158 
159         ///< TODO:清除SHA所有的寄存器
160 
161         sha->state.busy = 0U;
162     } else {
163         ret = CSI_ERROR;
164     }
165 
166     return ret;
167 }
168 
169 /**
170   \brief       De-initialize SHA Interface. stops operation and releases the software resources used by the interface
171   \param[in]   sha  sha handle to operate.
172   \return      none
173 */
csi_sha_uninit(csi_sha_t * sha)174 void csi_sha_uninit(csi_sha_t *sha)
175 {
176     CSI_PARAM_CHK_NORETVAL(sha);
177 
178     ///< TODO:清除SHA所有的寄存器
179 }
180 
181 /**
182   \brief       attach the callback handler to SHA
183   \param[in]   sha          operate handle.
184   \param[in]   callback     callback function
185   \param[in]   arg          callback's param
186   \return      error code
187 */
csi_sha_attach_callback(csi_sha_t * sha,void * callback,void * arg)188 csi_error_t csi_sha_attach_callback(csi_sha_t *sha, void *callback, void *arg)
189 {
190     CSI_PARAM_CHK(sha, CSI_ERROR);
191 
192     sha->callback = callback;
193     sha->arg = arg;
194     ///< 附着中断处理函数
195     csi_irq_attach((uint32_t)sha->dev.irq_num, &wj_sha_irq_handler, &sha->dev);
196     ///< 使能SHA中断
197     csi_irq_enable((uint32_t)sha->dev.irq_num);
198 
199     return CSI_OK;
200 }
201 
202 /**
203   \brief       detach the callback handler
204   \param[in]   sha  operate handle.
205 */
csi_sha_detach_callback(csi_sha_t * sha)206 void csi_sha_detach_callback(csi_sha_t *sha)
207 {
208     CSI_PARAM_CHK_NORETVAL(sha);
209     sha->callback = NULL;
210     sha->arg = NULL;
211     ///< 关闭SHA 中断功能
212     csi_irq_disable((uint32_t)sha->dev.irq_num);
213     ///< 清除SHA中断附着
214     csi_irq_detach((uint32_t)sha->dev.irq_num);
215 }
216 
217 /**
218   \brief       config sha mode.
219   \param[in]   sha     sha handle to operate.
220   \param[in]   context Pointer to the sha context
221   \param[in]   mode    sha mode \ref csi_sha_mode_t
222   \return      \ref csi_error_t
223 */
csi_sha_start(csi_sha_t * sha,csi_sha_context_t * context,csi_sha_mode_t mode)224 csi_error_t csi_sha_start(csi_sha_t *sha, csi_sha_context_t *context, csi_sha_mode_t mode)
225 {
226     CSI_PARAM_CHK(sha, CSI_ERROR);
227     CSI_PARAM_CHK(context, CSI_ERROR);
228     csi_error_t ret = CSI_OK;
229 
230     switch (mode) {
231         case SHA_MODE_1:
232         case SHA_MODE_256:
233         case SHA_MODE_224:
234         case SHA_MODE_512:
235         case SHA_MODE_384:
236             ///< TODO:设置SHA 模式
237             ///< TODO:清除 SHA   HASH 值
238             ///< TODO:清除 SHA   DATA 值
239 
240             memset((void *)&context->mode, 0, sizeof(csi_sha_context_t));
241             context->mode = mode;
242             ///< TODO:SHA 使能中断
243             ///< TODO:SHA 使能初始值
244 
245             ///< TODO:把SHA存储密文的寄存器中的内容复制sizeof(context->state)大小到context->state
246             break;
247 
248         case SHA_MODE_512_256:
249         case SHA_MODE_512_224:
250             ret = CSI_UNSUPPORTED;
251             break;
252 
253         default:
254             ret = CSI_ERROR;
255             break;
256     }
257 
258     return ret;
259 }
260 
261 /**
262   \brief       update the engine
263   \param[in]   sha     sha handle to operate.
264   \param[in]   context Pointer to the sha context
265   \param[in]   input   Pointer to the Source data
266   \param[in]   size    the data size
267   \return      \ref csi_error_t
268 */
csi_sha_update(csi_sha_t * sha,csi_sha_context_t * context,const void * input,uint32_t size)269 csi_error_t csi_sha_update(csi_sha_t *sha, csi_sha_context_t *context, const void *input, uint32_t size)
270 {
271     CSI_PARAM_CHK(sha, CSI_ERROR);
272     CSI_PARAM_CHK(context, CSI_ERROR);
273     csi_error_t ret = CSI_OK;
274 
275     uint8_t *p_data_in = (uint8_t *)input;
276     uint32_t block_size;
277     uint32_t timecount = 0U;
278     uint32_t left;
279     uint32_t length;
280 
281     sha->state.busy = 1U;
282 
283     ///< TODO:设置SHA 模式
284     ///< TODO:用context->state初始化HASH
285 
286     block_size = SHA_GET_BOLOCK_SIZE_BYTES(context->mode);
287 
288     context->total[0] += size;
289     left = strlen((const char *)context->buffer);               ///< Get unused message
290 
291     /**
292      * If there is any unused message, it will be added to the new message for calculation
293     */
294     if (left) {
295         memcpy((uint8_t *)context->buffer + left, p_data_in, SHA_ALG_SELECT_MIN(block_size - left, size)); ///< pad input message to complete block
296         size += left;                                           ///< input message size need add original message size(only unused)
297     } else {
298         memcpy((uint8_t *)context->buffer, p_data_in, SHA_ALG_SELECT_MIN(block_size, size));
299     }
300 
301     length = size;                                               ///< message size
302 
303     if (length >= block_size) {                                   ///< if length > block size, need accumulate two times least
304 
305         do {
306             ///< TODO:写入待计算值  context->buffer
307 
308             ///< TODO:开始SHA 计算
309 
310             mdelay(1U);                                          ///<! Must be kept, otherwise the data is prone to error
311 
312             length -= block_size;
313             p_data_in += (block_size - left);                    ///< input address offset
314             left = 0U;
315 
316             memcpy((uint8_t *)context->buffer, p_data_in, block_size);
317 
318             SHA_WAIT_IS_TIMEOUT(++timecount, ret);
319 
320             if (ret != CSI_OK) {
321                 break;
322             }
323         } while (length >= block_size);
324 
325         memset((uint8_t *)context->buffer, 0, block_size);
326 
327         memcpy((uint8_t *)context->buffer, p_data_in, (size & (block_size - 1U)));
328 
329     }
330 
331     ///< TODO:获取HASH用context->state
332 
333     sha->state.busy = 0U;
334 
335     return ret;
336 }
337 
338 /**
339   \brief       accumulate the engine (async mode)
340   \param[in]   sha     sha handle to operate.
341   \param[in]   context Pointer to the sha context
342   \param[in]   input   Pointer to the Source data
343   \param[in]   size    the data size
344   \return      \ref csi_error_t
345 */
csi_sha_update_async(csi_sha_t * sha,csi_sha_context_t * context,const void * input,uint32_t size)346 csi_error_t csi_sha_update_async(csi_sha_t *sha, csi_sha_context_t *context, const void *input, uint32_t size)
347 {
348     CSI_PARAM_CHK(sha, CSI_ERROR);
349     CSI_PARAM_CHK(context, CSI_ERROR);
350 
351     csi_error_t ret = CSI_UNSUPPORTED;
352 
353     return ret;
354 }
355 
356 /**
357   \brief       finish the engine
358   \param[in]   sha      sha handle to operate.
359   \param[in]   context Pointer to the sha context
360   \param[out]  output   Pointer to the result data
361   \param[out]  out_size Pointer to the result data size(bytes)
362   \return      \ref csi_error_t
363 */
csi_sha_finish(csi_sha_t * sha,csi_sha_context_t * context,void * output,uint32_t * out_size)364 csi_error_t csi_sha_finish(csi_sha_t *sha, csi_sha_context_t *context, void *output, uint32_t *out_size)
365 {
366     CSI_PARAM_CHK(sha, CSI_ERROR);
367     CSI_PARAM_CHK(context, CSI_ERROR);
368     csi_error_t ret = CSI_OK;
369 
370     uint32_t *p_data_in = (uint32_t *)context->buffer;
371     uint8_t *out_buf = (uint8_t *)output;
372     uint32_t i;
373     uint32_t msg_length;
374     uint32_t block_size;
375     uint32_t length;
376     uint32_t size = context->total[0];
377     uint32_t pad_buf[4] = {0U};
378     uint64_t pad_bit_len;
379 
380     ///< TODO:设置SHA 模式
381     ///< TODO:用context->state初始化HASH
382 
383     block_size = SHA_GET_BOLOCK_SIZE_BYTES(context->mode);
384     msg_length = SHA_GET_MSGLEN_TAIL_4BYTES(context->mode);
385 
386     pad_bit_len = (uint64_t)size << 3U;                                    ///< write message length into memory behind message
387 
388     length = (strlen((char *)context->buffer) + (msg_length << 2U)) + 1U;                          ///< message size + extra length + format(0x80)
389 
390     if (length > block_size) {
391         wj_sha_update_sub(sha, context, p_data_in, size);
392         memset((uint8_t *)context->buffer, 0, sizeof(context->buffer));
393     } else {
394         pad_buf[0] = 0x80U;
395         memcpy((uint8_t *)p_data_in + (size % block_size), &pad_buf[0], sizeof(uint32_t)); ///< add tail(msg bit length)
396     }
397 
398     pad_buf[1] = (uint32_t)pad_bit_len >> 16;
399     pad_buf[2] = (uint32_t)pad_bit_len;
400     wj_sha_alg_reverse((uint8_t *)&pad_buf[1], 2U);
401 
402     memcpy((uint8_t *)p_data_in + block_size - 8U, &pad_buf[1], sizeof(uint32_t) << 1); ///< add tail(msg bit length)
403 
404     ///< TODO:写入待计算值  p_data_in
405 
406     ///< TODO:开始SHA计算
407 
408     mdelay(1U);                                                 ///<! Must be kept, otherwise the data is prone to error
409 
410     *out_size = SHA_GET_MSGDIGEST_BYTES(context->mode);
411 
412     if (*out_size < SHA_GET_MSGDIGEST_BYTES(SHA_MODE_384)) {
413         ///< TODO:把SHA存储密文的寄存器中的内容复制out_size大小到context->buffer
414         wj_sha_alg_reverse(context->buffer, ((uint8_t)*out_size) >> 2U);   ///< Flip the last result data
415         memcpy(out_buf, context->buffer, *out_size);
416     } else {
417         ///< TODO:把SHA存储密文的寄存器中的内容复制64大小到context->buffer
418         wj_sha_alg_reverse(context->buffer, 16U);   ///< Flip the last result data
419 
420         for (i = 0U; i < (*out_size >> 2); i++) {
421             wj_sha_alg_sort(out_buf, context->buffer + (SHA_ALG_ALIGN_SORT(i) << 2), i);
422         }
423     }
424 
425     ///< TODO:开始SHA HASH
426     ///< TODO:开始SHA DATA
427 
428     /**
429      * clean cache
430     */
431     memset((uint8_t *)context->total, 0, sizeof(context->total));
432     memset((uint8_t *)context->state, 0, sizeof(context->state));
433     memset((uint8_t *)context->buffer, 0, sizeof(context->buffer));
434 
435     return ret;
436 }
437 
438 /**
439   \brief       Get SHA state.
440   \param[in]   sha  sha handle to operate.
441   \param[out]  state    sha state \ref csi_sha_state_t.
442   \return      \ref csi_error_t
443 */
csi_sha_get_state(csi_sha_t * sha,csi_sha_state_t * state)444 csi_error_t csi_sha_get_state(csi_sha_t *sha, csi_sha_state_t *state)
445 {
446     CSI_PARAM_CHK(sha, CSI_ERROR);
447     CSI_PARAM_CHK(state, CSI_ERROR);
448 
449     state->busy = sha->state.busy;
450     state->error = sha->state.error;
451 
452     return CSI_OK;
453 }
454 
455 #ifdef CONFIG_PM
wj_sha_pm_action(csi_dev_t * dev,csi_pm_dev_action_t action)456 csi_error_t wj_sha_pm_action(csi_dev_t *dev, csi_pm_dev_action_t action)
457 {
458     CSI_PARAM_CHK(dev, CSI_ERROR);
459 
460     csi_error_t ret = CSI_OK;
461     csi_pm_dev_t *pm_dev = &dev->pm_dev;
462 
463     switch (action) {
464         case PM_DEV_SUSPEND:
465             ///< TODO:恢复SHA 寄存器
466             break;
467 
468         case PM_DEV_RESUME:
469             ///< TODO:保存SHA 寄存器
470             break;
471 
472         default:
473             ret = CSI_ERROR;
474             break;
475     }
476 
477     return ret;
478 }
479 
csi_sha_enable_pm(csi_sha_t * sha)480 csi_error_t csi_sha_enable_pm(csi_sha_t *sha)
481 {
482     ///< TODO:注册 SHA 低功耗处理函数
483     return csi_pm_dev_register(&sha->dev, wj_sha_pm_action, 25U, 0U);
484 }
485 
csi_sha_disable_pm(csi_sha_t * sha)486 void csi_sha_disable_pm(csi_sha_t *sha)
487 {
488     csi_pm_dev_unregister(&sha->dev);
489 }
490 #endif
491