[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH v4 1/3] hw: Model ASPEED's Hash and Crypto Engine
From: |
Cédric Le Goater |
Subject: |
Re: [PATCH v4 1/3] hw: Model ASPEED's Hash and Crypto Engine |
Date: |
Wed, 24 Mar 2021 08:26:21 +0100 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.6.0 |
On 3/24/21 8:09 AM, Joel Stanley wrote:
> The HACE (Hash and Crypto Engine) is a device that offloads MD5, SHA1,
> SHA2, RSA and other cryptographic algorithms.
>
> This initial model implements a subset of the device's functionality;
> currently only direct access (non-scatter gather) hashing.
>
> Signed-off-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Thanks,
C.
> ---
> v3:
> - rebase on upstream to fix meson.build conflict
> v2:
> - reorder register defines
> - mask src/dest/len registers according to hardware
> v4:
> - Fix typos in comments
> - Remove sdram base address; new memory region fixes mean this is not
> required
> - Use PRIx64
> - Add Object Classes for soc familiy specific features
> - Convert big switch statement to a lookup in a struct
> ---
> include/hw/misc/aspeed_hace.h | 43 ++++
> hw/misc/aspeed_hace.c | 358 ++++++++++++++++++++++++++++++++++
> hw/misc/meson.build | 1 +
> 3 files changed, 402 insertions(+)
> create mode 100644 include/hw/misc/aspeed_hace.h
> create mode 100644 hw/misc/aspeed_hace.c
>
> diff --git a/include/hw/misc/aspeed_hace.h b/include/hw/misc/aspeed_hace.h
> new file mode 100644
> index 000000000000..94d5ada95fa2
> --- /dev/null
> +++ b/include/hw/misc/aspeed_hace.h
> @@ -0,0 +1,43 @@
> +/*
> + * ASPEED Hash and Crypto Engine
> + *
> + * Copyright (C) 2021 IBM Corp.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef ASPEED_HACE_H
> +#define ASPEED_HACE_H
> +
> +#include "hw/sysbus.h"
> +
> +#define TYPE_ASPEED_HACE "aspeed.hace"
> +#define TYPE_ASPEED_AST2400_HACE TYPE_ASPEED_HACE "-ast2400"
> +#define TYPE_ASPEED_AST2500_HACE TYPE_ASPEED_HACE "-ast2500"
> +#define TYPE_ASPEED_AST2600_HACE TYPE_ASPEED_HACE "-ast2600"
> +OBJECT_DECLARE_TYPE(AspeedHACEState, AspeedHACEClass, ASPEED_HACE)
> +
> +#define ASPEED_HACE_NR_REGS (0x64 >> 2)
> +
> +struct AspeedHACEState {
> + SysBusDevice parent;
> +
> + MemoryRegion iomem;
> + qemu_irq irq;
> +
> + uint32_t regs[ASPEED_HACE_NR_REGS];
> +
> + MemoryRegion *dram_mr;
> + AddressSpace dram_as;
> +};
> +
> +
> +struct AspeedHACEClass {
> + SysBusDeviceClass parent_class;
> +
> + uint32_t src_mask;
> + uint32_t dest_mask;
> + uint32_t hash_mask;
> +};
> +
> +#endif /* _ASPEED_HACE_H_ */
> diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c
> new file mode 100644
> index 000000000000..a962ccea90e8
> --- /dev/null
> +++ b/hw/misc/aspeed_hace.c
> @@ -0,0 +1,358 @@
> +/*
> + * ASPEED Hash and Crypto Engine
> + *
> + * Copyright (C) 2021 IBM Corp.
> + *
> + * Joel Stanley <joel@jms.id.au>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "qemu/error-report.h"
> +#include "hw/misc/aspeed_hace.h"
> +#include "qapi/error.h"
> +#include "migration/vmstate.h"
> +#include "crypto/hash.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/irq.h"
> +
> +#define R_CRYPT_CMD (0x10 / 4)
> +
> +#define R_STATUS (0x1c / 4)
> +#define HASH_IRQ BIT(9)
> +#define CRYPT_IRQ BIT(12)
> +#define TAG_IRQ BIT(15)
> +
> +#define R_HASH_SRC (0x20 / 4)
> +#define R_HASH_DEST (0x24 / 4)
> +#define R_HASH_SRC_LEN (0x2c / 4)
> +
> +#define R_HASH_CMD (0x30 / 4)
> +/* Hash algorithm selection */
> +#define HASH_ALGO_MASK (BIT(4) | BIT(5) | BIT(6))
> +#define HASH_ALGO_MD5 0
> +#define HASH_ALGO_SHA1 BIT(5)
> +#define HASH_ALGO_SHA224 BIT(6)
> +#define HASH_ALGO_SHA256 (BIT(4) | BIT(6))
> +#define HASH_ALGO_SHA512_SERIES (BIT(5) | BIT(6))
> +/* SHA512 algorithm selection */
> +#define SHA512_HASH_ALGO_MASK (BIT(10) | BIT(11) | BIT(12))
> +#define HASH_ALGO_SHA512_SHA512 0
> +#define HASH_ALGO_SHA512_SHA384 BIT(10)
> +#define HASH_ALGO_SHA512_SHA256 BIT(11)
> +#define HASH_ALGO_SHA512_SHA224 (BIT(10) | BIT(11))
> +/* HMAC modes */
> +#define HASH_HMAC_MASK (BIT(7) | BIT(8))
> +#define HASH_DIGEST 0
> +#define HASH_DIGEST_HMAC BIT(7)
> +#define HASH_DIGEST_ACCUM BIT(8)
> +#define HASH_HMAC_KEY (BIT(7) | BIT(8))
> +/* Cascaded operation modes */
> +#define HASH_ONLY 0
> +#define HASH_ONLY2 BIT(0)
> +#define HASH_CRYPT_THEN_HASH BIT(1)
> +#define HASH_HASH_THEN_CRYPT (BIT(0) | BIT(1))
> +/* Other cmd bits */
> +#define HASH_IRQ_EN BIT(9)
> +#define HASH_SG_EN BIT(18)
> +
> +static const struct {
> + uint32_t mask;
> + QCryptoHashAlgorithm algo;
> +} hash_algo_map[] = {
> + { HASH_ALGO_MD5, QCRYPTO_HASH_ALG_MD5 },
> + { HASH_ALGO_SHA1, QCRYPTO_HASH_ALG_SHA1 },
> + { HASH_ALGO_SHA224, QCRYPTO_HASH_ALG_SHA224 },
> + { HASH_ALGO_SHA256, QCRYPTO_HASH_ALG_SHA256 },
> + { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA512,
> QCRYPTO_HASH_ALG_SHA512 },
> + { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA384,
> QCRYPTO_HASH_ALG_SHA384 },
> + { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA256,
> QCRYPTO_HASH_ALG_SHA256 },
> +};
> +
> +static int hash_algo_lookup(uint32_t mask)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(hash_algo_map); i++) {
> + if (mask == hash_algo_map[i].mask)
> + return hash_algo_map[i].algo;
> + }
> +
> + return -1;
> +}
> +
> +static int do_hash_operation(AspeedHACEState *s, int algo)
> +{
> + hwaddr src, len, dest;
> + uint8_t *digest_buf = NULL;
> + size_t digest_len = 0;
> + char *src_buf;
> + int rc;
> +
> + src = s->regs[R_HASH_SRC];
> + len = s->regs[R_HASH_SRC_LEN];
> + dest = s->regs[R_HASH_DEST];
> +
> + src_buf = address_space_map(&s->dram_as, src, &len, false,
> + MEMTXATTRS_UNSPECIFIED);
> + if (!src_buf) {
> + qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map dram\n", __func__);
> + return -EACCES;
> + }
> +
> + rc = qcrypto_hash_bytes(algo, src_buf, len, &digest_buf, &digest_len,
> + &error_fatal);
> + if (rc < 0) {
> + qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto failed\n", __func__);
> + return rc;
> + }
> +
> + rc = address_space_write(&s->dram_as, dest, MEMTXATTRS_UNSPECIFIED,
> + digest_buf, digest_len);
> + if (rc) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: address space write failed\n", __func__);
> + }
> + g_free(digest_buf);
> +
> + address_space_unmap(&s->dram_as, src_buf, len, false, len);
> +
> + /*
> + * Set status bits to indicate completion. Testing shows hardware sets
> + * these irrespective of HASH_IRQ_EN.
> + */
> + s->regs[R_STATUS] |= HASH_IRQ;
> +
> + return 0;
> +}
> +
> +
> +static uint64_t aspeed_hace_read(void *opaque, hwaddr addr, unsigned int
> size)
> +{
> + AspeedHACEState *s = ASPEED_HACE(opaque);
> +
> + addr >>= 2;
> +
> + if (addr >= ASPEED_HACE_NR_REGS) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx
> "\n",
> + __func__, addr << 2);
> + return 0;
> + }
> +
> + return s->regs[addr];
> +}
> +
> +static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data,
> + unsigned int size)
> +{
> + AspeedHACEState *s = ASPEED_HACE(opaque);
> + AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s);
> +
> + addr >>= 2;
> +
> + if (addr >= ASPEED_HACE_NR_REGS) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx
> "\n",
> + __func__, addr << 2);
> + return;
> + }
> +
> + switch (addr) {
> + case R_STATUS:
> + if (data & HASH_IRQ) {
> + data &= ~HASH_IRQ;
> +
> + if (s->regs[addr] & HASH_IRQ) {
> + qemu_irq_lower(s->irq);
> + }
> + }
> + break;
> + case R_HASH_SRC:
> + data &= ahc->src_mask;
> + break;
> + case R_HASH_DEST:
> + data &= ahc->dest_mask;
> + break;
> + case R_HASH_SRC_LEN:
> + data &= 0x0FFFFFFF;
> + break;
> + case R_HASH_CMD: {
> + int algo = -1;
> + if ((data & HASH_HMAC_MASK)) {
> + qemu_log_mask(LOG_UNIMP,
> + "%s: HMAC engine command mode %"PRIx64" not
> implemented",
> + __func__, (data & HASH_HMAC_MASK) >> 8);
> + }
> + if (data & HASH_SG_EN) {
> + qemu_log_mask(LOG_UNIMP,
> + "%s: Hash scatter gather mode not implemented",
> + __func__);
> + }
> + if (data & BIT(1)) {
> + qemu_log_mask(LOG_UNIMP,
> + "%s: Cascaded mode not implemented",
> + __func__);
> + }
> + algo = hash_algo_lookup(data & ahc->hash_mask);
> + if (algo < 0) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: Invalid hash algorithm selection 0x%"PRIx64"\n",
> + __func__, data & ahc->hash_mask);
> + break;
> + }
> + do_hash_operation(s, algo);
> +
> + if (data & HASH_IRQ_EN) {
> + qemu_irq_raise(s->irq);
> + }
> + break;
> + }
> + case R_CRYPT_CMD:
> + qemu_log_mask(LOG_UNIMP, "%s: Crypt commands not implemented\n",
> + __func__);
> + break;
> + default:
> + break;
> + }
> +
> + s->regs[addr] = data;
> +}
> +
> +static const MemoryRegionOps aspeed_hace_ops = {
> + .read = aspeed_hace_read,
> + .write = aspeed_hace_write,
> + .endianness = DEVICE_LITTLE_ENDIAN,
> + .valid = {
> + .min_access_size = 1,
> + .max_access_size = 4,
> + },
> +};
> +
> +static void aspeed_hace_reset(DeviceState *dev)
> +{
> + struct AspeedHACEState *s = ASPEED_HACE(dev);
> +
> + memset(s->regs, 0, sizeof(s->regs));
> +}
> +
> +static void aspeed_hace_realize(DeviceState *dev, Error **errp)
> +{
> + AspeedHACEState *s = ASPEED_HACE(dev);
> + SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> +
> + sysbus_init_irq(sbd, &s->irq);
> +
> + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_hace_ops, s,
> + TYPE_ASPEED_HACE, 0x1000);
> +
> + if (!s->dram_mr) {
> + error_setg(errp, TYPE_ASPEED_HACE ": 'dram' link not set");
> + return;
> + }
> +
> + address_space_init(&s->dram_as, s->dram_mr, "dram");
> +
> + sysbus_init_mmio(sbd, &s->iomem);
> +}
> +
> +static Property aspeed_hace_properties[] = {
> + DEFINE_PROP_LINK("dram", AspeedHACEState, dram_mr,
> + TYPE_MEMORY_REGION, MemoryRegion *),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +
> +static const VMStateDescription vmstate_aspeed_hace = {
> + .name = TYPE_ASPEED_HACE,
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32_ARRAY(regs, AspeedHACEState, ASPEED_HACE_NR_REGS),
> + VMSTATE_END_OF_LIST(),
> + }
> +};
> +
> +static void aspeed_hace_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> +
> + dc->realize = aspeed_hace_realize;
> + dc->reset = aspeed_hace_reset;
> + device_class_set_props(dc, aspeed_hace_properties);
> + dc->vmsd = &vmstate_aspeed_hace;
> +}
> +
> +static const TypeInfo aspeed_hace_info = {
> + .name = TYPE_ASPEED_HACE,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(AspeedHACEState),
> + .class_init = aspeed_hace_class_init,
> + .class_size = sizeof(AspeedHACEClass)
> +};
> +
> +static void aspeed_ast2400_hace_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass);
> +
> + dc->desc = "AST2400 Hash and Crypto Engine";
> +
> + ahc->src_mask = 0x0FFFFFFF;
> + ahc->dest_mask = 0x0FFFFFF8;
> + ahc->hash_mask = HASH_ALGO_MASK;
> +}
> +
> +static const TypeInfo aspeed_ast2400_hace_info = {
> + .name = TYPE_ASPEED_AST2400_HACE,
> + .parent = TYPE_ASPEED_HACE,
> + .class_init = aspeed_ast2400_hace_class_init,
> +};
> +
> +static void aspeed_ast2500_hace_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass);
> +
> + dc->desc = "AST2500 Hash and Crypto Engine";
> +
> + ahc->src_mask = 0x3fffffff;
> + ahc->dest_mask = 0x3ffffff8;
> + ahc->hash_mask = HASH_ALGO_MASK;
> +}
> +
> +static const TypeInfo aspeed_ast2500_hace_info = {
> + .name = TYPE_ASPEED_AST2500_HACE,
> + .parent = TYPE_ASPEED_HACE,
> + .class_init = aspeed_ast2500_hace_class_init,
> +};
> +
> +static void aspeed_ast2600_hace_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass);
> +
> + dc->desc = "AST2600 Hash and Crypto Engine";
> +
> + ahc->src_mask = 0x7FFFFFFF;
> + ahc->dest_mask = 0x7FFFFFF8;
> + ahc->hash_mask = HASH_ALGO_MASK | SHA512_HASH_ALGO_MASK;
> +}
> +
> +static const TypeInfo aspeed_ast2600_hace_info = {
> + .name = TYPE_ASPEED_AST2600_HACE,
> + .parent = TYPE_ASPEED_HACE,
> + .class_init = aspeed_ast2600_hace_class_init,
> +};
> +
> +static void aspeed_hace_register_types(void)
> +{
> + type_register_static(&aspeed_ast2400_hace_info);
> + type_register_static(&aspeed_ast2500_hace_info);
> + type_register_static(&aspeed_ast2600_hace_info);
> + type_register_static(&aspeed_hace_info);
> +}
> +
> +type_init(aspeed_hace_register_types);
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index 21034dc60a81..1e7b8b064bd1 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -109,6 +109,7 @@ softmmu_ss.add(when: 'CONFIG_PVPANIC_ISA', if_true:
> files('pvpanic-isa.c'))
> softmmu_ss.add(when: 'CONFIG_PVPANIC_PCI', if_true: files('pvpanic-pci.c'))
> softmmu_ss.add(when: 'CONFIG_AUX', if_true: files('auxbus.c'))
> softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files(
> + 'aspeed_hace.c',
> 'aspeed_lpc.c',
> 'aspeed_scu.c',
> 'aspeed_sdmc.c',
>