blob: 59b7c9ec3483f4040027153d80c8ae4c5a34c738 [file] [log] [blame]
#include <common.h>
#include <sha256.h>
#include <config.h>
#include <asm/arch/cpu.h>
#include <asm/io.h>
#include <malloc.h>
#include <sha256.h>
#include "asr_bcm.h"
#include "asr_hash.h"
static hash_state md;
static void hash_sw_reset(void)
{
uint32_t val;
val = (0x1 << 0x3);
bcm_write32(HASH_CONTROL, val);
val = 0x0;
bcm_write32(HASH_CONTROL, val);
return;
}
static int hash_set_mode(HASH_MODE_T mode, HASH_ALGO_T algo)
{
uint32_t val;
val = bcm_read32(HASH_CONFIG);
val &= ~0xf;
val |= algo;
if (mode == HASH_HMAC)
val |= (0x1 << 0x3);
bcm_write32(HASH_CONFIG, val);
return 0;
}
static int hash_kick(void)
{
uint32_t val;
uint32_t cnt;
val = bcm_read32(HASH_COMMAND);
val |= (0x1 << 0x0);
bcm_write32(HASH_COMMAND, val);
cnt = 1;
/* wait for command */
do {
val = bcm_read32(HASH_STATUS);
if (cnt == 1000000) {
printf("hash kick wait busy %u times..0x%08x\n", cnt, val);
return -1;
}
val &= 0xE;
udelay(1);
cnt++;
} while(val != 0);
cnt = 1;
do {
val = bcm_read32(HASH_STATUS);
if (cnt == 1000000) {
printf("hash kick wait busy %u times..0x%08x\n", cnt, val);
return -1;
}
val &= 0x1;
udelay(1);
cnt++;
} while(val == 0);
/* clear status so next command can be issued */
bcm_write32(HASH_STATUS, val);
return 0;
}
static int hash_config_op(HASH_OP_MODE_T op_mode)
{
uint32_t val;
int ret = 0;
if (op_mode < HASH_INIT || op_mode > HASH_FINAL)
return -1;
val = bcm_read32(HASH_CONTROL);
val &= ~(0x3 << 0x0);
val |= op_mode;
bcm_write32(HASH_CONTROL, val);
ret = hash_kick();
return ret;
}
static int hash_save_context(hash_state *md, int alg)
{
int i;
switch(alg) {
case HASH_SHA384:
case HASH_SHA512:
for (i = 0; i < 8; i++) {
md->sha512.state[i] = bcm_read32(HASH_DIGEST(i));
md->sha512.state[i+8] = bcm_read32(HASH_DIGEST_H(i));
}
break;
case HASH_SHA256:
case HASH_SHA224:
for (i = 0; i < 8; i++) {
md->sha256.state[i] = bcm_read32(HASH_DIGEST(i));
}
break;
case HASH_SHA1:
for (i = 0; i < 5; i++) {
md->sha1.state[i] = bcm_read32(HASH_DIGEST(i));
}
break;
case HASH_MD5:
for (i = 0; i < 4; i++) {
md->md5.state[i] = bcm_read32(HASH_DIGEST(i));
}
break;
default:
printf("hash save context: invalid alg!\r\n");
return -1;
}
return 0;
}
static int hash_restore_context(hash_state *md, int alg)
{
int i;
switch(alg) {
case HASH_SHA384:
case HASH_SHA512:
for (i = 0; i < 8; i++) {
bcm_write32(HASH_DIGEST(i), md->sha512.state[i]);
bcm_write32(HASH_DIGEST_H(i), md->sha512.state[i+8]);
}
break;
case HASH_SHA256:
case HASH_SHA224:
for (i = 0; i < 8; i++) {
bcm_write32(HASH_DIGEST(i), md->sha256.state[i]);
}
break;
case HASH_SHA1:
for (i = 0; i < 5; i++) {
bcm_write32(HASH_DIGEST(i), md->sha1.state[i]);
}
break;
case HASH_MD5:
for (i = 0; i < 4; i++) {
bcm_write32(HASH_DIGEST(i), md->md5.state[i]);
}
break;
default:
printf("hash restore context: invalid alg!\r\n");
return -1;
}
return 0;
}
static int hash_compress_aligned(hash_state *md, int alg, uint8_t *in, int data_len)
{
int ret = 0;
if (((uint32_t)in & 0x3) || (data_len == 0))
return -1;
adec_engine_hw_reset(ACC_ENG_DMA);
abus_set_mode(ABUS_GRP_A_HASH, ABUS_GRP_B_AES, ABUS_CROSS, ABUS_STRAIGHT);
dma_input_config(0, 0);
ret = hash_restore_context(md, alg);
if (ret)
goto error;
ret = dma_input_address((uint32_t)virt_to_phys((void *)in), \
ROUND_UP_TO_WORD_CNT(data_len), 0);
if (ret)
goto error;
flush_dcache_range(in, (uint32_t)in + 4*ROUND_UP_TO_WORD_CNT(data_len));
dma_input_start();
bcm_write32(HASH_INCOME_SEG_SZ, data_len);
ret = hash_config_op(HASH_UPDATE);
if (ret) {
dma_input_stop();
goto error;
}
dma_wait_input_finish();
dma_input_stop();
ret = hash_save_context(md, alg);
if (ret)
goto error;
error:
return ret;
}
static int hash_compress(hash_state *md, int alg, uint8_t *in, int blks, int blk_sz)
{
uint8_t *dma_in = NULL;
int data_len = blks * blk_sz;
int ret, n;
uint8_t *ptr_in;
if (((uint32_t)in & 0x3) == 0) {
dma_in = in;
ret = hash_compress_aligned(md, alg, dma_in, data_len);
return ret;
}
n = MIN(data_len, HASH_ALIGN_BUF_SIZE);
dma_in = (uint8_t *)malloc(n + 0x10);
if (!dma_in) {
ret = -1;
goto exit;
}
dma_in = (uint8_t *)(((uint32_t)(dma_in)) & (~0x3));
ptr_in = in;
do {
n = MIN(data_len, HASH_ALIGN_BUF_SIZE);
memcpy((void *)dma_in, (void *)ptr_in, n);
ret = hash_compress_aligned(md, alg, dma_in, n);
if (ret) {
goto exit;
}
data_len -= n;
ptr_in +=n;
} while(data_len > 0);
exit:
if (dma_in)
free(dma_in);
return ret;
}
static int hash_tail_process(hash_state *md, uint8_t *out, int out_size, \
uint64_t total_size, int tail_size, unsigned char *dma_addr, int alg)
{
int ret = 0;
int reg_val, i;
adec_engine_hw_reset(ACC_ENG_DMA);
abus_set_mode(ABUS_GRP_A_HASH, ABUS_GRP_B_AES, ABUS_CROSS, ABUS_STRAIGHT);
dma_input_config(0, 0);
ret = hash_restore_context(md, alg);
if (ret)
goto error;
ret = dma_input_address((uint32_t)virt_to_phys((void *)dma_addr), \
ROUND_UP_TO_WORD_CNT(tail_size), 0);
if (ret)
goto error;
if (tail_size) {
flush_dcache_range(dma_addr, (uint32_t)dma_addr + 4*ROUND_UP_TO_WORD_CNT(tail_size));
dma_input_start();
}
bcm_write32(HASH_INCOME_SEG_SZ, tail_size);
bcm_write32(HASH_TOTAL_MSG_SZ_L, (total_size & 0xffffffff));
bcm_write32(HASH_TOTAL_MSG_SZ_H, (total_size >> 32));
reg_val = bcm_read32(HASH_CONTROL);
reg_val |= (0x1 << 0x2);
bcm_write32(HASH_CONTROL, reg_val);
ret = hash_config_op(HASH_FINAL);
if (ret) {
if (tail_size)
dma_input_stop();
goto error;
}
if (tail_size) {
dma_wait_input_finish();
dma_input_stop();
}
/* copy digest out */
if (alg == HASH_SHA384 || alg == HASH_SHA512) {
for (i = 0; i < (out_size / 8); i++) {
reg_val = bcm_read32(HASH_DIGEST(i));
out[4 + i * 8] = (uint8_t)(reg_val & 0xFF);
out[5 + i * 8] = (uint8_t)((reg_val >> 8) & 0xFF);
out[6 + i * 8] = (uint8_t)((reg_val >> 16) & 0xFF);
out[7 + i * 8] = (uint8_t)((reg_val >> 24) & 0xFF);
reg_val = bcm_read32(HASH_DIGEST_H(i));
out[0 + i * 8] = (uint8_t)(reg_val & 0xFF);
out[1 + i * 8] = (uint8_t)((reg_val >> 8) & 0xFF);
out[2 + i * 8] = (uint8_t)((reg_val >> 16) & 0xFF);
out[3 + i * 8] = (uint8_t)((reg_val >> 24) & 0xFF);
}
} else {
for (i = 0; i < (out_size / 4); i++) {
reg_val = bcm_read32(HASH_DIGEST(i));
out[0 + i * 4] = (uint8_t)(reg_val & 0xFF);
out[1 + i * 4] = (uint8_t)((reg_val >> 8) & 0xFF);
out[2 + i * 4] = (uint8_t)((reg_val >> 16) & 0xFF);
out[3 + i * 4] = (uint8_t)((reg_val >> 24) & 0xFF);
}
}
error:
bcm_enable(0);
return ret;
}
static int hash_init(hash_state *md, int alg)
{
int ret;
if (md) {
memset(md, 0, sizeof(hash_state));
md->block_size = BLOCK_ALGIN_SIZE;
} else
return -1;
bcm_enable(1);
adec_engine_hw_reset(ACC_ENG_HASH);
hash_sw_reset();
ret = hash_set_mode(HASH_SIMPLE, alg);
if (ret)
goto error;
ret = hash_config_op(HASH_INIT);
if (ret)
goto error;
ret = hash_save_context(md, alg);
if (ret)
goto error;
return 0;
error:
bcm_enable(0);
return ret;
}
/* Only block algnie is processed at a time */
static int hash_process(hash_state *md, int alg, uint8_t *in, uint32_t inlen)
{
uint32_t n, blocks;
int err;
if (md->curlen > sizeof(md->buf)) {
return -1;
}
while (inlen > 0) {
if (md->curlen == 0 && inlen >= md->block_size) {
blocks = inlen / md->block_size;
err = hash_compress(md, alg, in, blocks, md->block_size);
if (err)
return err;
md->length += blocks * md->block_size * 8;
in += blocks * md->block_size;
inlen -= blocks * md->block_size;
} else {
n = MIN(inlen, (md->block_size - md->curlen));
memcpy(md->buf + md->curlen, in, n);
md->curlen += n;
in += n;
inlen -= n;
if (md->curlen == md->block_size) {
err = hash_compress(md, alg, in, md->buf, md->block_size);
if (err)
return err;
md->length += 8*md->block_size;
md->curlen = 0;
}
}
}
return 0;
}
static int hash_done(hash_state *md, int alg, uint8_t *out)
{
uint32_t out_len;
switch(alg) {
case HASH_SHA512:
out_len = HASH_LEN_SHA512;
break;
case HASH_SHA384:
out_len = HASH_LEN_SHA384;
break;
case HASH_SHA256:
out_len = HASH_LEN_SHA256;
break;
case HASH_SHA224:
out_len = HASH_LEN_SHA224;
break;
case HASH_SHA1:
out_len = HASH_LEN_SHA1;
break;
case HASH_MD5:
out_len = HASH_LEN_MD5;
break;
default:
printf("err: not support hash alg\n");
return -1;
}
return hash_tail_process(md, out, out_len, \
(md->length / 8 + md->curlen), md->curlen, md->buf, alg);
}
void sha256_starts_bcm(sha256_context *ctx)
{
hash_init(&md, HASH_SHA256);
return;
}
void sha256_update_bcm(sha256_context *ctx, const uint8_t *input, uint32_t length)
{
hash_process(&md, HASH_SHA256, input, length);
return;
}
void sha256_finish_bcm(sha256_context *ctx, uint8_t digest[32])
{
hash_done(&md, HASH_SHA256, digest);
return;
}
#ifdef BCM_HASH_SELFTEST
static int hash_handle(int alg, uint8_t *in, uint32_t inlen, uint8_t *out)
{
int ret = 0;
ret = hash_init(&md, alg);
if (ret)
return ret;
ret = hash_process(&md, alg, in, inlen);
if (ret)
return ret;
ret = hash_done(&md, alg, out);
if (ret)
return ret;
return ret;
}
static int sha1(uint8_t *in, uint32_t inlen, uint8_t *out)
{
return hash_handle(HASH_SHA1, in, inlen, out);
}
static int sha256(uint8_t *in, uint32_t inlen, uint8_t *out)
{
return hash_handle(HASH_SHA256, in, inlen, out);
}
int bcm_sha_test(void)
{
int ret = 0;
uint32_t i;
unsigned char out_sha1[20] = {0};
unsigned char out_sha256[32] = {0};
const struct {
const char *msg;
uint8_t hash[20];
} sha1_tests[] = {
{
"abc",
{ 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06,
0x81, 0x6a, 0xba, 0x3e, 0x25, 0x71,
0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0,
0xd8, 0x9d
}
},
{
"asjhsdjljfdsdjjkdfwyqeuwouzxkmcxjkmwqds"
"jklfdfjlkdfkfsfkjlfskjdflioherfjjfdjkfd"
"nkfdfdojjodfjdfjflj;sljjlfkklnfnkgbhhoi"
"gfhigfopojpfjojpoffkjlfskjdflioherfjjfd"
"jkfdnkfdfdojjodfjdfjfljnfnkgbhhoigfhigf"
"oponfnkgbhhoigfhigfopojpfjoewiroiowiods"
"djkisijdknknkskdnknflnnesniewinoinknmdn"
"kknknsdnjjfsnnkfnkknslnklknfnknkflksnlk"
"lskldklklklnmlflmlmlfmlfml",
{
0xc4, 0x53, 0xca, 0x24, 0xfa, 0xe5,
0x39, 0x53, 0x08, 0x8c, 0x57, 0x1a,
0x96, 0xe9, 0x64, 0x7f, 0xd5, 0xf9,
0x13, 0x91
}
}
};
const struct {
const char *msg;
uint8_t hash[32];
} sha256_tests[] = {
{
"abc",
{ 0xBA, 0x78, 0x16, 0xBF, 0x8F, 0x01,
0xCF, 0xEA, 0x41, 0x41, 0x40, 0xDE,
0x5D, 0xAE, 0x22, 0x23, 0xB0, 0x03,
0x61, 0xA3, 0x96, 0x17, 0x7A, 0x9C,
0xB4, 0x10, 0xFF, 0x61, 0xF2, 0x00,
0x15, 0xAD
}
},
{
"asjhsdjljfdsdjjkdfwyqeuwouzxkmcxjkmwq",
{ 0x4d, 0xcb, 0x3b, 0xd5, 0x54, 0x96,
0xb7, 0xaa, 0xf8, 0xee, 0x2e, 0x28,
0x28, 0x29, 0x9c, 0x6b, 0xda, 0x1a,
0xdf, 0x5a, 0x21, 0x64, 0x17, 0xc7,
0xc7, 0x9e, 0x33, 0x2c, 0x99, 0xb5,
0x28, 0x3f
}
}
};
uint32_t start, time;
for (i = 0; i < sizeof(sha1_tests) / sizeof(sha1_tests[0]); i++) {
start = get_ticks();
ret = sha1(sha1_tests[i].msg, strlen(sha1_tests[i].msg), out_sha1);
time = get_ticks();
if (ret)
return ret;
printf("sha1 hw T = %d\r\n", (time-start));
if (memcmp(out_sha1, sha1_tests[i].hash, sizeof(out_sha1))) {
printf("sha1 test %d failed\r\n", i);
} else {
printf("sha1 test %d pass\r\n", i);
}
}
for (i = 0; i < sizeof(sha256_tests) / sizeof(sha256_tests[0]); i++) {
start = get_ticks();
ret = sha256(sha256_tests[i].msg, strlen(sha256_tests[i].msg), out_sha256);
time = get_ticks();
if (ret)
return ret;
printf("sha256 hw T = %d\r\n", (time-start));
if (memcmp(out_sha256, sha256_tests[i].hash, sizeof(out_sha256))) {
printf("sha256 test %d failed\r\n", i);
} else {
printf("sha256 test %d pass\r\n", i);
}
}
return 0;
}
#endif