| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright (c) 2024 Nuvoton Technology Corp. |
| */ |
| |
| #include <common.h> |
| #include <dm.h> |
| #include <hash.h> |
| #include <malloc.h> |
| #include <asm/io.h> |
| #include <linux/iopoll.h> |
| |
| #define SHA512_BLOCK_LENGTH (1024 / 8) |
| |
| /* Register fields */ |
| #define HASH_CTR_STS_SHA_EN BIT(0) |
| #define HASH_CTR_STS_SHA_BUSY BIT(1) |
| #define HASH_CTR_STS_SHA_RST BIT(2) |
| #define HASH_CFG_SHA1_SHA2 BIT(0) |
| #define SHA512_CMD_SHA_512 BIT(3) |
| #define SHA512_CMD_INTERNAL_ROUND BIT(2) |
| #define SHA512_CMD_WRITE BIT(1) |
| #define SHA512_CMD_READ BIT(0) |
| |
| enum { |
| type_sha1 = 0, |
| type_sha256, |
| type_sha384, |
| type_sha512, |
| }; |
| |
| struct npcm_sha_regs { |
| u8 data_in; |
| u8 data_out; |
| u8 ctr_sts; |
| u8 hash_cfg; |
| u8 sha512_cmd; |
| }; |
| |
| struct hash_info { |
| u32 block_sz; |
| u32 digest_len; |
| u8 length_bytes; |
| u8 type; |
| }; |
| |
| struct message_block { |
| u64 length[2]; |
| u64 nonhash_sz; |
| u8 buffer[SHA512_BLOCK_LENGTH * 2]; |
| }; |
| |
| struct npcm_sha_priv { |
| void *base; |
| struct npcm_sha_regs *regs; |
| struct hash_info *hash; |
| struct message_block block; |
| bool internal_round; |
| bool support_sha512; |
| }; |
| |
| static struct npcm_sha_regs npcm_sha_reg_tbl[] = { |
| { .data_in = 0x0, .data_out = 0x20, .ctr_sts = 0x4, .hash_cfg = 0x8 }, |
| { .data_in = 0x10, .data_out = 0x1c, .ctr_sts = 0x14, .sha512_cmd = 0x18 }, |
| }; |
| |
| static struct hash_info npcm_hash_tbl[] = { |
| { .type = type_sha1, .block_sz = 64, .digest_len = 160, .length_bytes = 8 }, |
| { .type = type_sha256, .block_sz = 64, .digest_len = 256, .length_bytes = 8 }, |
| { .type = type_sha384, .block_sz = 128, .digest_len = 384, .length_bytes = 16 }, |
| { .type = type_sha512, .block_sz = 128, .digest_len = 512, .length_bytes = 16 }, |
| }; |
| |
| static struct npcm_sha_priv *sha_priv; |
| |
| static int npcm_sha_init(u8 type) |
| { |
| struct message_block *block = &sha_priv->block; |
| |
| if (type > type_sha512 || |
| (!sha_priv->support_sha512 && |
| (type == type_sha384 || type == type_sha512))) |
| return -ENOTSUPP; |
| |
| sha_priv->regs = &npcm_sha_reg_tbl[type / 2]; |
| sha_priv->hash = &npcm_hash_tbl[type]; |
| block->length[0] = 0; |
| block->length[1] = 0; |
| block->nonhash_sz = 0; |
| sha_priv->internal_round = false; |
| |
| return 0; |
| } |
| |
| static void npcm_sha_reset(void) |
| { |
| struct npcm_sha_regs *regs = sha_priv->regs; |
| struct hash_info *hash = sha_priv->hash; |
| u8 val; |
| |
| if (hash->type == type_sha1) |
| writeb(HASH_CFG_SHA1_SHA2, sha_priv->base + regs->hash_cfg); |
| else if (hash->type == type_sha256) |
| writeb(0, sha_priv->base + regs->hash_cfg); |
| else if (hash->type == type_sha384) |
| writeb(0, sha_priv->base + regs->sha512_cmd); |
| else if (hash->type == type_sha512) |
| writeb(SHA512_CMD_SHA_512, sha_priv->base + regs->sha512_cmd); |
| |
| val = readb(sha_priv->base + regs->ctr_sts) & ~HASH_CTR_STS_SHA_EN; |
| writeb(val | HASH_CTR_STS_SHA_RST, sha_priv->base + regs->ctr_sts); |
| } |
| |
| static void npcm_sha_enable(bool on) |
| { |
| struct npcm_sha_regs *regs = sha_priv->regs; |
| u8 val; |
| |
| val = readb(sha_priv->base + regs->ctr_sts) & ~HASH_CTR_STS_SHA_EN; |
| val |= on; |
| writeb(val | on, sha_priv->base + regs->ctr_sts); |
| } |
| |
| static int npcm_sha_flush_block(u8 *block) |
| { |
| struct npcm_sha_regs *regs = sha_priv->regs; |
| struct hash_info *hash = sha_priv->hash; |
| u32 *blk_dw = (u32 *)block; |
| u8 val; |
| int i; |
| |
| if (readb_poll_timeout(sha_priv->base + regs->ctr_sts, val, |
| !(val & HASH_CTR_STS_SHA_BUSY), 100)) |
| return -ETIMEDOUT; |
| |
| if (hash->type == type_sha384 || hash->type == type_sha512) { |
| val = SHA512_CMD_WRITE; |
| if (hash->type == type_sha512) |
| val |= SHA512_CMD_SHA_512; |
| if (sha_priv->internal_round) |
| val |= SHA512_CMD_INTERNAL_ROUND; |
| writeb(val, sha_priv->base + regs->sha512_cmd); |
| } |
| for (i = 0; i < (hash->block_sz / sizeof(u32)); i++) |
| writel(blk_dw[i], sha_priv->base + regs->data_in); |
| |
| sha_priv->internal_round = true; |
| |
| return 0; |
| } |
| |
| static int npcm_sha_update_block(const u8 *in, u32 len) |
| { |
| struct message_block *block = &sha_priv->block; |
| struct hash_info *hash = sha_priv->hash; |
| u8 *buffer = &block->buffer[0]; |
| u32 block_sz = hash->block_sz; |
| u32 hash_sz; |
| |
| hash_sz = (block->nonhash_sz + len) > block_sz ? |
| (block_sz - block->nonhash_sz) : len; |
| memcpy(buffer + block->nonhash_sz, in, hash_sz); |
| block->nonhash_sz += hash_sz; |
| block->length[0] += hash_sz; |
| if (block->length[0] < hash_sz) |
| block->length[1]++; |
| |
| if (block->nonhash_sz == block_sz) { |
| block->nonhash_sz = 0; |
| if (npcm_sha_flush_block(buffer)) |
| return -EBUSY; |
| } |
| |
| return hash_sz; |
| } |
| |
| static int npcm_sha_update(const u8 *input, u32 len) |
| { |
| int hash_sz; |
| |
| while (len) { |
| hash_sz = npcm_sha_update_block(input, len); |
| if (hash_sz < 0) { |
| printf("SHA512 module busy\n"); |
| return -EBUSY; |
| } |
| len -= hash_sz; |
| input += hash_sz; |
| } |
| |
| return 0; |
| } |
| |
| static int npcm_sha_finish(u8 *out) |
| { |
| struct npcm_sha_regs *regs = sha_priv->regs; |
| struct message_block *block = &sha_priv->block; |
| struct hash_info *hash = sha_priv->hash; |
| u8 *buffer = &block->buffer[0]; |
| u32 block_sz = hash->block_sz; |
| u32 *out32 = (u32 *)out; |
| u32 zero_len, val; |
| u64 *length; |
| u8 reg_data_out; |
| int i; |
| |
| /* Padding, minimal padding size is last_byte+length_bytes */ |
| if ((block_sz - block->nonhash_sz) >= (hash->length_bytes + 1)) |
| zero_len = block_sz - block->nonhash_sz - (hash->length_bytes + 1); |
| else |
| zero_len = block_sz * 2 - block->nonhash_sz - (hash->length_bytes + 1); |
| /* Last byte */ |
| buffer[block->nonhash_sz++] = 0x80; |
| /* Zero bits padding */ |
| memset(&buffer[block->nonhash_sz], 0, zero_len); |
| block->nonhash_sz += zero_len; |
| /* Message length */ |
| length = (u64 *)&buffer[block->nonhash_sz]; |
| if (hash->length_bytes == 16) { |
| *length++ = cpu_to_be64(block->length[1] << 3 | block->length[0] >> 61); |
| block->nonhash_sz += 8; |
| } |
| *length = cpu_to_be64(block->length[0] << 3); |
| block->nonhash_sz += 8; |
| if (npcm_sha_flush_block(&block->buffer[0])) |
| return -ETIMEDOUT; |
| |
| /* After padding, the last message may produce 2 blocks */ |
| if (block->nonhash_sz > block_sz) { |
| if (npcm_sha_flush_block(&block->buffer[block_sz])) |
| return -ETIMEDOUT; |
| } |
| /* Read digest */ |
| if (readb_poll_timeout(sha_priv->base + regs->ctr_sts, val, |
| !(val & HASH_CTR_STS_SHA_BUSY), 100)) |
| return -ETIMEDOUT; |
| if (hash->type == type_sha384) |
| writeb(SHA512_CMD_READ, sha_priv->base + regs->sha512_cmd); |
| else if (hash->type == type_sha512) |
| writeb(SHA512_CMD_SHA_512 | SHA512_CMD_READ, |
| sha_priv->base + regs->sha512_cmd); |
| |
| reg_data_out = regs->data_out; |
| for (i = 0; i < (hash->digest_len / 32); i++) { |
| *out32 = readl(sha_priv->base + reg_data_out); |
| out32++; |
| if (hash->type == type_sha1 || hash->type == type_sha256) |
| reg_data_out += 4; |
| } |
| |
| return 0; |
| } |
| |
| int npcm_sha_calc(const u8 *input, u32 len, u8 *output, u8 type) |
| { |
| if (npcm_sha_init(type)) |
| return -ENOTSUPP; |
| npcm_sha_reset(); |
| npcm_sha_enable(true); |
| npcm_sha_update(input, len); |
| npcm_sha_finish(output); |
| npcm_sha_enable(false); |
| |
| return 0; |
| } |
| |
| void hw_sha512(const unsigned char *input, unsigned int len, |
| unsigned char *output, unsigned int chunk_sz) |
| { |
| if (!sha_priv->support_sha512) { |
| puts(" HW accelerator not support\n"); |
| return; |
| } |
| puts(" using BMC HW accelerator\n"); |
| npcm_sha_calc(input, len, output, type_sha512); |
| } |
| |
| void hw_sha384(const unsigned char *input, unsigned int len, |
| unsigned char *output, unsigned int chunk_sz) |
| { |
| if (!sha_priv->support_sha512) { |
| puts(" HW accelerator not support\n"); |
| return; |
| } |
| puts(" using BMC HW accelerator\n"); |
| npcm_sha_calc(input, len, output, type_sha384); |
| } |
| |
| void hw_sha256(const unsigned char *input, unsigned int len, |
| unsigned char *output, unsigned int chunk_sz) |
| { |
| puts(" using BMC HW accelerator\n"); |
| npcm_sha_calc(input, len, output, type_sha256); |
| } |
| |
| void hw_sha1(const unsigned char *input, unsigned int len, |
| unsigned char *output, unsigned int chunk_sz) |
| { |
| puts(" using BMC HW accelerator\n"); |
| npcm_sha_calc(input, len, output, type_sha1); |
| } |
| |
| int hw_sha_init(struct hash_algo *algo, void **ctxp) |
| { |
| if (!strcmp("sha1", algo->name)) { |
| npcm_sha_init(type_sha1); |
| } else if (!strcmp("sha256", algo->name)) { |
| npcm_sha_init(type_sha256); |
| } else if (!strcmp("sha384", algo->name)) { |
| if (!sha_priv->support_sha512) |
| return -ENOTSUPP; |
| npcm_sha_init(type_sha384); |
| } else if (!strcmp("sha512", algo->name)) { |
| if (!sha_priv->support_sha512) |
| return -ENOTSUPP; |
| npcm_sha_init(type_sha512); |
| } else { |
| return -ENOTSUPP; |
| } |
| |
| printf("Using npcm SHA engine\n"); |
| npcm_sha_reset(); |
| npcm_sha_enable(true); |
| |
| return 0; |
| } |
| |
| int hw_sha_update(struct hash_algo *algo, void *ctx, const void *buf, |
| unsigned int size, int is_last) |
| { |
| return npcm_sha_update(buf, size); |
| } |
| |
| int hw_sha_finish(struct hash_algo *algo, void *ctx, void *dest_buf, |
| int size) |
| { |
| int ret; |
| |
| ret = npcm_sha_finish(dest_buf); |
| npcm_sha_enable(false); |
| |
| return ret; |
| } |
| |
| static int npcm_sha_bind(struct udevice *dev) |
| { |
| sha_priv = calloc(1, sizeof(struct npcm_sha_priv)); |
| if (!sha_priv) |
| return -ENOMEM; |
| |
| sha_priv->base = dev_read_addr_ptr(dev); |
| if (!sha_priv->base) { |
| printf("Cannot find sha reg address, binding failed\n"); |
| return -EINVAL; |
| } |
| |
| if (IS_ENABLED(CONFIG_ARCH_NPCM8XX)) |
| sha_priv->support_sha512 = true; |
| |
| printf("SHA: NPCM SHA module bind OK\n"); |
| |
| return 0; |
| } |
| |
| static const struct udevice_id npcm_sha_ids[] = { |
| { .compatible = "nuvoton,npcm845-sha" }, |
| { .compatible = "nuvoton,npcm750-sha" }, |
| { } |
| }; |
| |
| U_BOOT_DRIVER(npcm_sha) = { |
| .name = "npcm_sha", |
| .id = UCLASS_MISC, |
| .of_match = npcm_sha_ids, |
| .priv_auto = sizeof(struct npcm_sha_priv), |
| .bind = npcm_sha_bind, |
| }; |