/** ****************************************************************************** * @file hgsha.c * @author HUGE-IC Application Team * @version V1.0.0 * @date * @brief sha256 ****************************************************************************** * @attention * *

© COPYRIGHT 2019 HUGE-IC

* * * ****************************************************************************** */ #include "typesdef.h" #include "list.h" #include "errno.h" #include "dev.h" #include "devid.h" #include "osal/string.h" #include "osal/semaphore.h" #include "osal/mutex.h" #include "osal/irq.h" #include "osal/string.h" #include "osal/sleep.h" #include "dev/sha/hgsha_v1.h" #include "hgsha_v1_hw.h" #include "hal/sha.h" #if 1 #define SHA_PRINTF(fmt, arg...) printf(fmt, ##arg) #else #define SHA_PRINTF(fmt, arg...) #endif typedef enum { SHA1_INIT = BIT(3) | BIT(1), SHA1_UPDATE = BIT(3), SHA1_FINISH = BIT(3) | BIT(2), SHA2_INIT = BIT(1), SHA2_UPDATE = 0, SHA2_FINISH = BIT(2), SHA_INTR_EN = BIT(0), } SHA_HWMODE; #define SHA_NONBLOCK (0x1000) /******************************** * * LOW LAYER FUNCTION AREA * * *****************************/ static inline void ll_sha_get_result_big_endian(struct hgsha_v1_hw *hw, uint8 *buf) { volatile uint32_t *tmp = (uint32_t *) & (hw->SHA_RESULT0); uint32_t tmp_dat = 0; for (uint32_t i = 0; i < 8; i++, tmp++) { tmp_dat = *tmp; *buf++ = (tmp_dat ) & 0xFF; *buf++ = (tmp_dat >> 8) & 0xFF; *buf++ = (tmp_dat >> 16) & 0xFF; *buf++ = (tmp_dat >> 24) & 0xFF; } } static inline void ll_sha_get_result_little_endian(struct hgsha_v1_hw *hw, uint8 *buf) { volatile uint32_t *tmp = (uint32_t *) & (hw->SHA_RESULT0); uint32_t tmp_dat = 0; for (uint32_t i = 0; i < 8; i++, tmp++) { tmp_dat = *tmp; *buf++ = (tmp_dat >> 24) & 0xFF; *buf++ = (tmp_dat >> 16) & 0xFF; *buf++ = (tmp_dat >> 8) & 0xFF; *buf++ = (tmp_dat ) & 0xFF; } } static void ll_sha_calc(struct hgsha_v1_hw *sha, uint8_t *buf, uint16_t len, SHA_HWMODE mode) { sha->SHA_STADDR = (uint32)buf; sha->SHA_BYTELEN = len; sha->SHA_PENDING = 1; sha->SHA_CONFIG = mode; sha->SHA_START = 1; // while(!sha->SHA_PENDING); } /******************************** * * DRIVE LAYER FUNCTION AREA * * *****************************/ static void hgsha_irq_handle(void *args) { struct hgsha_v1 *sha = (struct hgsha_v1 *)args; struct hgsha_v1_hw *sha_reg = (struct hgsha_v1_hw *)sha->hw; sha_reg->SHA_PENDING = 1; os_sema_up(&sha->done); if (sha->irq_func) { sha->irq_func(sha->irq_data); } } static int32_t hgsha_v1_request_irq(struct sha_dev *dev, void * irq_handle, void *args) { struct hgsha_v1 *sha = (struct hgsha_v1 *)dev; sha->irq_func = args; sha->irq_data = irq_handle; return RET_OK; } static int hgsha_v1_release_irq(struct sha_dev *dev) { struct hgsha_v1 *sha = (struct hgsha_v1 *)dev; sha->irq_func = NULL; return RET_OK; } static int hgsha_v1_transform(struct sha_dev *dev, struct sha_req *req) { struct hgsha_v1 *sha = (struct hgsha_v1 *)dev; struct hgsha_v1_hw *hw = (struct hgsha_v1_hw *)sha->hw; int ret = 0; uint8_t *buf = req->input; uint32_t blocks = req->len / 64; uint32_t stas = 0; SHA_HWMODE mode = 0; if (os_mutex_lock(&sha->lock, 4000)) { printf("sha get mutex failure\r\n"); return RET_ERR; } switch (req->type) { case T_SHA1: stas = 5 * 4; mode = SHA1_UPDATE; break; case T_SHA256: stas = 8 * 4; mode = SHA2_UPDATE; break; default: return -1; } mode |= SHA_INTR_EN; memcpy((void*)&hw->SHA_RESULT0, req->state, stas); sys_dcache_clean_range_unaligned((void*)req->input, req->len); os_sema_eat(&sha->done); for (int bs = 512;bs>8;bs/=2) { for (;blocks>=bs;) { ll_sha_calc(hw, buf, bs * 64, mode); buf += bs * 64; blocks -= bs; ret |= !os_sema_down(&sha->done, 8000); } } if (blocks) { uint64_t tick = os_jiffies(); mode &= ~SHA_INTR_EN; ll_sha_calc(hw, buf, blocks * 64, mode); while(!hw->SHA_PENDING) { if (os_jiffies()-tick >= 8000) { ret = 1; break; } } } memcpy(req->state, (void*)&hw->SHA_RESULT0, stas); os_mutex_unlock(&sha->lock); return ret; } int __sha_init(struct sha_dev *dev, struct sha_ctx * ctx) { if (ctx->type == T_SHA1) { ctx->state[0] = 0x67452301; ctx->state[1] = 0xEFCDAB89; ctx->state[2] = 0x98BADCFE; ctx->state[3] = 0x10325476; ctx->state[4] = 0xC3D2E1F0; } else { ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a; ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19; } ctx->dlen = 0; ctx->bit_len = 0; return 0; } int __sha_update(struct sha_dev *sha, struct sha_ctx *ctx, uint8_t *buf, uint32_t len) { struct sha_req req = { .state = ctx->state, .type = ctx->type, }; if (ctx->dlen) { if (ctx->dlen + len >= 64) { memcpy(&ctx->buf[ctx->dlen], buf, 64 - ctx->dlen); buf += 64 - ctx->dlen; len -= 64 - ctx->dlen; req.input = ctx->buf; req.len = 64; hgsha_v1_transform(sha, &req); ctx->bit_len += req.len * 8; ctx->dlen = 0; } else { memcpy(&ctx->buf[ctx->dlen], buf, len); ctx->dlen += len; len = 0; } } for (int i = 512; i != 0; i /= 2) { while (len / 64 >= i) { req.input = buf; req.len = i * 64; hgsha_v1_transform(sha, &req); ctx->bit_len += req.len * 8; buf += i * 64; len -= i * 64; } } if (len) { memcpy(ctx->buf, buf, len); ctx->dlen = len; } return 0; } int __sha_final(struct sha_dev *sha, struct sha_ctx *ctx, uint8_t *hash) { struct sha_req req = { .state = ctx->state, .type = ctx->type, .input = ctx->buf, .len = 64, }; // Pad whatever data is left in the buffer. int i = ctx->dlen; if (ctx->dlen < 56) { ctx->buf[i++] = 0x80; while (i < 56) ctx->buf[i++] = 0x00; } else { ctx->buf[i++] = 0x80; while (i < 64) ctx->buf[i++] = 0x00; hgsha_v1_transform(sha, &req); memset(ctx->buf, 0, 56); } // Append to the padding the total message's length in bits and transform. ctx->bit_len += ctx->dlen * 8; ctx->buf[63] = ctx->bit_len; ctx->buf[62] = ctx->bit_len >> 8; ctx->buf[61] = ctx->bit_len >> 16; ctx->buf[60] = ctx->bit_len >> 24; ctx->buf[59] = ctx->bit_len >> 32; ctx->buf[58] = ctx->bit_len >> 40; ctx->buf[57] = ctx->bit_len >> 48; ctx->buf[56] = ctx->bit_len >> 56; hgsha_v1_transform(sha, &req); // Since this implementation uses little endian uint8_t ordering and SHA uses big endian, // reverse all the bytes when copying the final state to the output hash. for (i = 0; i < 4; ++i) { hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; if (ctx->type == T_SHA256) { hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; } } return 0; } static int hgsha_v1_init(struct sha_dev *dev, SHA_TYPE type) { struct hgsha_v1 *sha = (struct hgsha_v1 *)dev; sha->ctx.type = type; sha->ctx.bit_len = 0; sha->ctx.dlen = 0; __sha_init(dev, sha->pctx); return 0; } static int32 hgsha_v1_update(struct sha_dev *dev, uint8 *input, uint32 len) { struct hgsha_v1 *sha = (struct hgsha_v1 *)dev; return __sha_update(dev, sha->pctx, input, len); } static int32 hgsha_v1_final(struct sha_dev *dev, uint8 *output) { struct hgsha_v1 *sha = (struct hgsha_v1 *)dev; return __sha_final(dev, sha->pctx, output); } static int32 hgsha_v1_ioctl(struct sha_dev *dev, uint32 cmd, uint32 param1, uint32 param2) { struct hgsha_v1 *sha = (struct hgsha_v1 *)dev; switch (cmd) { case SHA_IOCTL_CMD_START: os_mutex_lock(&sha->lock, osWaitForever); break; case SHA_IOCTL_CMD_END: os_mutex_unlock(&sha->lock); break; case SHA_IOCTL_CMD_RESET: break; case SHA_IOCTL_GET_CTX: *((struct sha_ctx*)param1) = sha->ctx; break; case SHA_IOCTL_PUT_CTX: sha->ctx = *((struct sha_ctx*)param1); break; case SHA_IOCTL_RELOAD_CTX: sha->pctx = (struct sha_ctx*)param1; break; default: return RET_ERR; } return RET_OK; } void hg_sha_test_printf(struct sha_dev *dev) { uint8_t src[64]; uint32_t rsum = 0; uint32_t state[8] = { 0x12345678, 0x66225544, 0x11447788, 0x22441597, 0x22441597, 0x11447788, 0x66225544, 0x12345678 }; for (int i = 0;i<64;i++) src[i] = i; struct sha_req req = { .type = T_SHA256, .input = src, .state = state, .len = 64, }; hgsha_v1_transform(dev, &req); for (int i = 0;i<8;i++) rsum += state[i]; _os_printf("sha test: %x\r\n", rsum); if (rsum != 0x4a635c1c) { _os_printf("sha lp err\r\n"); } } #ifdef CONFIG_SLEEP // #define HGSHA_SLEEP_TEST(dev) hg_sha_test_printf(dev) #define HGSHA_SLEEP_TEST(dev) static int32 hgsha_v1_suspend(struct sha_dev *dev) { struct hgsha_v1 *sha =(struct hgsha_v1 *)dev; struct hgsha_v1_hw *sha_reg = sha->hw; HGSHA_SLEEP_TEST(dev); if(os_mutex_lock(&sha->lock, 80000)) { return RET_ERR; } irq_disable(sha->irq_num); sysctrl_sha_clk_close(); return 0; } static int32 hgsha_v1_resume(struct sha_dev *dev) { struct hgsha_v1 *sha =(struct hgsha_v1 *)dev; struct hgsha_v1_hw *hw = (struct hgsha_v1_hw *)sha->hw; os_mutex_unlock(&sha->lock); sysctrl_sha_clk_open(); sysctrl_sha_reset(); hw->SHA_PENDING |= 1; irq_enable(sha->irq_num); HGSHA_SLEEP_TEST(dev); return 0; } #endif static const struct sha_hal_ops sha_v1_ops = { .init = hgsha_v1_init, .update = hgsha_v1_update, .final = hgsha_v1_final, .xform = hgsha_v1_transform, .ioctl = hgsha_v1_ioctl, .requset_irq = hgsha_v1_request_irq, .release_irq = hgsha_v1_release_irq, #ifdef CONFIG_SLEEP .ops.suspend = (int32 (*)(struct dev_obj *obj))hgsha_v1_suspend, .ops.resume = (int32 (*)(struct dev_obj *obj))hgsha_v1_resume, #endif }; __init int32_t hgsha_v1_attach(uint32_t dev_id, struct hgsha_v1 *sha) { sha->dev.dev.ops = (const struct devobj_ops *)&sha_v1_ops; struct hgsha_v1_hw *hw = (struct hgsha_v1_hw *)sha->hw; //clear pending hw->SHA_PENDING |= 1; sha->irq_func = NULL; sha->flags = 0; sha->pctx = &sha->ctx; os_mutex_init(&sha->lock); os_sema_init(&sha->done, 0); request_irq(sha->irq_num, hgsha_irq_handle, sha); irq_enable(sha->irq_num); dev_register(dev_id, (struct dev_obj *)sha); return RET_OK; }