// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2024 Nuvoton Technology Corp. */ #include #include #include #include #include #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, };