mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-26 07:17:10 +00:00

As part of bringing the master branch back in to next, we need to allow for all of these changes to exist here. Reported-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Tom Rini <trini@konsulko.com>
376 lines
9.2 KiB
C
376 lines
9.2 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (c) 2024 Nuvoton Technology Corp.
|
|
*/
|
|
|
|
#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,
|
|
};
|