hw/misc: Implement a generic SHA driver for newer Espressif targets

This commit is contained in:
harshal.patil
2025-02-03 11:40:53 +05:30
committed by Ivan Grokhotkov
parent 3418debeda
commit 24970c0d2c
2 changed files with 549 additions and 0 deletions
+383
View File
@@ -0,0 +1,383 @@
/*
* ESP SHA accelerator
*
* Copyright (c) 2025 Espressif Systems (Shanghai) Co. Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 or
* (at your option) any later version.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "hw/registerfields.h"
#include "hw/dma/esp_gdma.h"
#include "hw/misc/esp_sha.h"
#include "hw/irq.h"
#define SHA_WARNING 0
#define SHA_DEBUG 0
static ESPHashAlg esp_sha_algs[] = {
[ESP_SHA_1_MODE] = {
.init = (hash_init) sha1_init,
.compress = (hash_compress) sha1_compress,
.len = sizeof(struct sha1_state)
},
[ESP_SHA_224_MODE] = {
.init = (hash_init) sha224_init,
.compress = (hash_compress) sha224_compress,
.len = SHA224_HASH_SIZE
},
[ESP_SHA_256_MODE] = {
.init = (hash_init) sha256_init,
.compress = (hash_compress) sha256_compress,
.len = sizeof(struct sha256_state)
},
[ESP_SHA_384_MODE] = {
.init = (hash_init) sha384_init,
.compress = (hash_compress) sha512_compress,
.len = SHA384_HASH_SIZE
},
[ESP_SHA_512_MODE] = {
.init = (hash_init) sha512_init,
.compress = (hash_compress) sha512_compress,
.len = sizeof(struct sha512_state)
},
[ESP_SHA_512_224_MODE] = {
.init = (hash_init) sha512_224_init,
.compress = (hash_compress) sha512_compress,
.len = sizeof(struct sha512_state)
},
[ESP_SHA_512_256_MODE] = {
.init = (hash_init) sha512_256_init,
.compress = (hash_compress) sha512_compress,
.len = sizeof(struct sha512_state)
},
[ESP_SHA_512_t_MODE] = {
.init = (hash_init) sha512_t_init,
.init_message = (hash_init_message) sha512_t_init_message,
.compress = (hash_compress) sha512_compress,
.len = sizeof(struct sha512_state)
},
};
static void esp_sha_write_digest(ESPShaMode mode, uint32_t* hash, ESPHashContext* context, size_t len)
{
if (mode < ESP_SHA_384_MODE) {
memcpy(context, hash, len);
} else {
for (int i = 0; i < 8; i++) {
context->sha512.state[i] = ((uint64_t)hash[i * 2] << 32) | hash[1 + (i * 2)];
}
}
}
static void esp_sha_read_digest(ESPShaMode mode, uint32_t* hash, ESPHashContext* context, size_t len)
{
if (mode < ESP_SHA_384_MODE) {
memcpy(hash, context, len);
} else {
for (int i = 0; i < 8; i++) {
hash[i * 2] = (uint32_t)(context->sha512.state[i] >> 32);
hash[1 + (i * 2)] = (uint32_t)(context->sha512.state[i] & 0xffffffff);
}
}
}
static void esp_sha_continue_hash(ESPShaState *s, uint32_t mode, uint32_t *message, uint32_t *hash)
{
assert(mode <= ESP_SHA_512_t_MODE);
ESPHashAlg alg = esp_sha_algs[mode];
alg.compress(&s->context, (uint8_t*) message);
esp_sha_read_digest(mode, hash, &s->context, alg.len);
}
static void esp_sha_continue_dma(ESPShaState *s)
{
assert(s->mode <= ESP_SHA_512_t_MODE);
ESPHashAlg alg = esp_sha_algs[s->mode];
uint32_t gdma_out_idx = 0;
assert(alg.compress);
size_t blk_len = (s->mode < ESP_SHA_384_MODE) ? 64 : 128;
/* Number of blocks to process, each block is blk_len bytes big */
const uint32_t blocks = s->block;
const uint32_t buf_size = blocks * blk_len;
/* Get the GDMA channel connected to SHA module.
* Specify ESP_GDMA_OUT_IDX since the data are going OUT of GDMA but IN our current component. */
if ( !esp_gdma_get_channel_periph(s->gdma, GDMA_SHA, ESP_GDMA_OUT_IDX, &gdma_out_idx) )
{
warn_report("[SHA] GDMA requested but no properly configured channel found");
return;
}
/* Allocate the buffer that will contain the data and get teh actual data */
uint8_t *buffer = g_malloc(blocks * blk_len);
if (buffer == NULL)
{
error_report("[SHA] No more memory in host!");
return;
}
if ( !esp_gdma_read_channel(s->gdma, gdma_out_idx, buffer, buf_size) ) {
warn_report("[SHA] Error reading from GDMA buffer");
g_free(buffer);
return;
}
/* Perform the actual SHA operation on the whole buffer */
for (uint32_t i = 0; i < blocks; i++)
{
alg.compress(&s->context, buffer + i * blk_len);
}
esp_sha_read_digest(s->mode, s->hash, &s->context, alg.len);
g_free(buffer);
/* Trigger an interrupt if enabled! */
if (s->int_ena) {
qemu_irq_raise(s->irq);
}
}
static void esp_sha_start(ESPShaState *s, ESPShaOperation op, uint32_t mode, uint32_t *message, uint32_t *hash)
{
ESPHashAlg alg = esp_sha_algs[mode];
assert(alg.init && alg.compress);
if ((op & SHA_OP_TYPE_MASK) == OP_START) {
alg.init(&s->context);
if (s->mode == ESP_SHA_512_t_MODE) {
alg.init_message(message, ESP_SHA_MAX_MESSAGE_WORDS, s->t, s->t_len);
}
} else {
/* Continue operation: initialize the context from the current hash.
* We don't have any accessor to do it so ... do it the "dirty" way */
esp_sha_write_digest(mode, hash, &s->context, alg.len);
}
if ((op & SHA_OP_DMA_MASK) == SHA_OP_DMA_MASK) {
esp_sha_continue_dma(s);
} else {
esp_sha_continue_hash(s, mode, message, hash);
}
}
static uint64_t esp_sha_read(void *opaque, hwaddr addr, unsigned int size)
{
ESPShaClass *class = ESP_SHA_GET_CLASS(opaque);
ESPShaState *s = ESP_SHA(opaque);
hwaddr index = 0;
uint64_t r = 0;
switch (addr) {
case A_SHA_MODE:
r = s->mode;
break;
case A_SHA_BUSY:
/* SHA driver is never busy as calculation happens synchronously */
r = 0;
break;
case A_SHA_DATE:
/* Hardcode the version control register for now */
r = class->date;
break;
case A_SHA_H_MEM ... A_SHA_M_MEM - 1:
index = (addr - A_SHA_H_MEM) / sizeof(uint32_t);
r = bswap32(s->hash[index]);
break;
case A_SHA_M_MEM ... ESP_SHA_REGS_SIZE - 1:
index = (addr - A_SHA_M_MEM) / sizeof(uint32_t);
r = s->message[index];
break;
case A_SHA_DMA_BLOCK_NUM:
r = s->block;
break;
case A_SHA_IRQ_ENA:
r = s->int_ena ? 1 : 0;
break;
default:
#if SHA_WARNING
warn_report("[ESP] SHA DMA and IRQ unsupported for now, ignoring...\n");
#endif
break;
}
#if SHA_DEBUG
info_report("[ESP] SHA reading %08lx (%08lx)", addr, r);
#endif
return r;
}
static void esp_sha_write(void *opaque, hwaddr addr,
uint64_t value, unsigned int size)
{
ESPShaClass *class = ESP_SHA_GET_CLASS(opaque);
ESPShaState *s = ESP_SHA(opaque);
hwaddr index = 0;
#if SHA_DEBUG
info_report("[ESP] SHA writing %08lx (%08lx)", addr, value);
#endif
switch (addr) {
case A_SHA_MODE:
/* Make sure the value is always between 0 and 7 as the real hardware doesn't
* accept a value of 8. Choose SHA-1 by default in that case. */
s->mode = (value & 0b111) % 8;
break;
case A_SHA_T_STRING:
s->t = bswap32((uint32_t) value);
break;
case A_SHA_T_LENGTH:
s->t_len = bswap32(FIELD_EX32(value, SHA_T_LENGTH, T_LENGTH));
break;
case A_SHA_START:
if (FIELD_EX32(value, SHA_START, START)) {
class->sha_start(s, OP_START, s->mode, s->message, s->hash);
}
break;
case A_SHA_CONTINUE:
if (FIELD_EX32(value, SHA_CONTINUE, CONTINUE)) {
class->sha_start(s, OP_CONTINUE, s->mode, s->message, s->hash);
}
break;
case A_SHA_H_MEM ... A_SHA_M_MEM - 1:
/* Only support word aligned access for the moment */
if (size != sizeof(uint32_t)) {
error_report("[SHA] Only 32-bit word access supported at the moment");
}
index = (addr - A_SHA_H_MEM) / sizeof(uint32_t);
s->hash[index] = bswap32((uint32_t) value);
break;
case A_SHA_M_MEM ... ESP_SHA_REGS_SIZE - 1:
index = (addr - A_SHA_M_MEM) / sizeof(uint32_t);
s->message[index] = (uint32_t) value;
break;
case A_SHA_DMA_BLOCK_NUM:
s->block = FIELD_EX32(value, SHA_DMA_BLOCK_NUM, DMA_BLOCK_NUM);
break;
case A_SHA_DMA_START:
if (FIELD_EX32(value, SHA_DMA_START, DMA_START)) {
class->sha_start(s, OP_DMA_START, s->mode, s->message, s->hash);
}
break;
case A_SHA_DMA_CONTINUE:
if (FIELD_EX32(value, SHA_DMA_CONTINUE, DMA_CONTINUE)) {
class->sha_start(s, OP_DMA_CONTINUE, s->mode, s->message, s->hash);
}
break;
case A_SHA_CLEAR_IRQ:
qemu_irq_lower(s->irq);
break;
case A_SHA_IRQ_ENA:
s->int_ena = FIELD_EX32(value, SHA_IRQ_ENA, INTERRUPT_ENA) != 0;
break;
default:
#if SHA_WARNING
/* Unsupported for now, do nothing */
warn_report("[SHA] Unsupported write to %08lx\n", addr);
#endif
break;
}
}
static const MemoryRegionOps esp_sha_ops = {
.read = esp_sha_read,
.write = esp_sha_write,
.endianness = DEVICE_LITTLE_ENDIAN,
};
static void esp_sha_reset(DeviceState *dev)
{
ESPShaState *s = ESP_SHA(dev);
memset(s->hash, 0, 8 * sizeof(uint32_t));
memset(s->message, 0, ESP_SHA_MAX_MESSAGE_WORDS * sizeof(uint32_t));
s->block = 0;
s->int_ena = 0;
qemu_irq_lower(s->irq);
}
static void esp_sha_realize(DeviceState *dev, Error **errp)
{
ESPShaState *s = ESP_SHA(dev);
/* Make sure GDMA was set of issue an error */
if (s->gdma == NULL) {
error_report("[SHA] GDMA controller must be set!");
}
}
static void esp_sha_init(Object *obj)
{
ESPShaState *s = ESP_SHA(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->iomem, obj, &esp_sha_ops, s,
TYPE_ESP_SHA, ESP_SHA_REGS_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
}
static void esp_sha_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ESPShaClass* esp_sha = ESP_SHA_CLASS(klass);
dc->realize = esp_sha_realize;
dc->legacy_reset = esp_sha_reset;
esp_sha->sha_start = esp_sha_start;
}
static const TypeInfo esp_sha_info = {
.name = TYPE_ESP_SHA,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(ESPShaState),
.instance_init = esp_sha_init,
.class_init = esp_sha_class_init,
.class_size = sizeof(ESPShaClass),
.abstract = true
};
static void esp_sha_register_types(void)
{
type_register_static(&esp_sha_info);
}
type_init(esp_sha_register_types)
+166
View File
@@ -0,0 +1,166 @@
/*
* ESP SHA emulation
*
* Copyright (c) 2025 Espressif Systems (Shanghai) Co. Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 or
* (at your option) any later version.
*/
#pragma once
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "hw/registerfields.h"
#include "crypto/sha512_t_i.h"
#include "crypto/sha512_256_i.h"
#include "crypto/sha512_224_i.h"
#include "crypto/sha512_i.h"
#include "crypto/sha384_i.h"
#include "crypto/sha256_i.h"
#include "crypto/sha224_i.h"
#include "crypto/sha1_i.h"
#include "hw/dma/esp_gdma.h"
#define TYPE_ESP_SHA "misc.esp.sha"
#define ESP_SHA(obj) OBJECT_CHECK(ESPShaState, (obj), TYPE_ESP_SHA)
#define ESP_SHA_GET_CLASS(obj) OBJECT_GET_CLASS(ESPShaClass, obj, TYPE_ESP_SHA)
#define ESP_SHA_CLASS(klass) OBJECT_CLASS_CHECK(ESPShaClass, klass, TYPE_ESP_SHA)
#define ESP_SHA_REGS_SIZE (0x100)
/**
* @brief Size of the message array, in bytes
*/
#define ESP_SHA_MAX_MESSAGE_SIZE 128
#define ESP_SHA_MAX_MESSAGE_WORDS (ESP_SHA_MAX_MESSAGE_SIZE / sizeof(uint32_t))
/**
* @brief Mode configuration for the SHA_MODE register.
*/
typedef enum {
ESP_SHA_1_MODE = 0,
ESP_SHA_224_MODE = 1,
ESP_SHA_256_MODE = 2,
ESP_SHA_384_MODE = 3,
ESP_SHA_512_MODE = 4,
ESP_SHA_512_224_MODE = 5,
ESP_SHA_512_256_MODE = 6,
ESP_SHA_512_t_MODE = 7,
} ESPShaMode;
#define SHA_OP_TYPE_MASK (1 << 0)
#define SHA_OP_DMA_MASK (1 << 1)
typedef enum {
OP_START = 0,
OP_CONTINUE = 1,
OP_DMA_START = SHA_OP_DMA_MASK | OP_START,
OP_DMA_CONTINUE = SHA_OP_DMA_MASK | OP_CONTINUE,
} ESPShaOperation;
typedef union {
struct sha512_state sha512;
struct sha256_state sha256;
struct sha1_state sha1;
} ESPHashContext;
typedef void (*hash_init)(void *);
typedef void (*hash_init_message)(uint32_t *, size_t, uint32_t, uint32_t);
typedef void (*hash_compress)(void *, const uint8_t*);
typedef struct {
hash_init init;
hash_init_message init_message;
/* For all types of hash, the message to "compress" must be 64-byte long (16 words of 32 bits) */
hash_compress compress;
/* Length of the context in bytes */
size_t len;
} ESPHashAlg;
typedef struct ESPShaState {
SysBusDevice parent_obj;
MemoryRegion iomem;
/* SHA mode selected by the application */
ESPShaMode mode;
/* Context for the hash calculation */
ESPHashContext context;
/* Resulted hash value */
uint32_t hash[16];
/* User data value */
uint32_t message[ESP_SHA_MAX_MESSAGE_WORDS];
uint32_t t;
uint32_t t_len;
/* DMA related */
/* Number of block to process in DMA mode */
uint32_t block;
bool int_ena;
qemu_irq irq;
/* Public: must be set before realizing instance*/
ESPGdmaState *gdma;
} ESPShaState;
typedef struct ESPShaClass {
SysBusDeviceClass parent_class;
/* Target specific registers */
uint32_t message_len;
/* Version register */
uint32_t date;
/* Virtual methods*/
void (*sha_start)(ESPShaState *s, ESPShaOperation op, uint32_t mode, uint32_t *message, uint32_t *hash);
} ESPShaClass;
REG32(SHA_MODE, 0x000)
FIELD(SHA_MODE, MODE, 0, 3)
REG32(SHA_T_STRING, 0x004)
REG32(SHA_T_LENGTH, 0x008)
FIELD(SHA_T_LENGTH, T_LENGTH, 0, 7)
REG32(SHA_DMA_BLOCK_NUM, 0x00C)
FIELD(SHA_DMA_BLOCK_NUM, DMA_BLOCK_NUM, 0, 6)
REG32(SHA_START, 0x010)
FIELD(SHA_START, START, 0, 1)
REG32(SHA_CONTINUE, 0x014)
FIELD(SHA_CONTINUE, CONTINUE, 0, 1)
REG32(SHA_BUSY, 0x018)
FIELD(SHA_BUSY, BUSY_STATE, 0, 1)
REG32(SHA_DMA_START, 0x01C)
FIELD(SHA_DMA_START, DMA_START, 0, 1)
REG32(SHA_DMA_CONTINUE, 0x020)
FIELD(SHA_DMA_CONTINUE, DMA_CONTINUE, 0, 1)
REG32(SHA_CLEAR_IRQ, 0x024)
FIELD(SHA_CLEAR_IRQ, CLEAR_INTERRUPT, 0, 1)
REG32(SHA_IRQ_ENA, 0x028)
FIELD(SHA_IRQ_ENA, INTERRUPT_ENA, 0, 1)
REG32(SHA_DATE, 0x02C)
FIELD(SHA_DATE, DATE, 0, 30)
REG32(SHA_H_MEM, 0x040)
REG32(SHA_M_MEM, 0x080)