This commit implements AES encryption/decryption functionality for the MAX78000 SOC
Signed-off-by: Jackson Donaldson <jc...@duck.com> --- hw/arm/Kconfig | 1 + hw/arm/max78000_soc.c | 12 +- hw/misc/Kconfig | 3 + hw/misc/max78000_aes.c | 215 +++++++++++++++++++++++++++++++++ hw/misc/max78000_gcr.c | 9 +- hw/misc/meson.build | 1 + include/hw/arm/max78000_soc.h | 2 + include/hw/misc/max78000_aes.h | 68 +++++++++++ include/hw/misc/max78000_gcr.h | 19 +++ 9 files changed, 326 insertions(+), 4 deletions(-) create mode 100644 hw/misc/max78000_aes.c create mode 100644 include/hw/misc/max78000_aes.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index c13ddc980a..5d38aa0dc3 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -371,6 +371,7 @@ config MAX78000_SOC select MAX78000_UART select MAX78000_GCR select MAX78000_TRNG + select MAX78000_AES config RASPI bool diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c index 6809d4e249..4b9682afce 100644 --- a/hw/arm/max78000_soc.c +++ b/hw/arm/max78000_soc.c @@ -41,6 +41,8 @@ static void max78000_soc_initfn(Object *obj) object_initialize_child(obj, "trng", &s->trng, TYPE_MAX78000_TRNG); + object_initialize_child(obj, "aes", &s->aes, TYPE_MAX78000_AES); + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0); } @@ -138,6 +140,13 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(armv7m, 4)); dev->id = g_strdup("trng"); + dev = DEVICE(&s->aes); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x40007400); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(armv7m, 5)); + dev->id = g_strdup("aes"); + + create_unimplemented_device("systemInterface", 0x40000400, 0x400); create_unimplemented_device("functionControl", 0x40000800, 0x3400); create_unimplemented_device("watchdogTimer0", 0x40003000, 0x400); @@ -149,9 +158,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("powerSequencer", 0x40006800, 0x400); create_unimplemented_device("miscControl", 0x40006c00, 0x800); - create_unimplemented_device("aes", 0x40007400, 0x400); - create_unimplemented_device("aesKey", 0x40007800, 0x800); - create_unimplemented_device("gpio0", 0x40008000, 0x1000); create_unimplemented_device("gpio1", 0x40009000, 0x1000); diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index dd6a6e54da..c27285b47a 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -47,6 +47,9 @@ config A9SCU config ARM11SCU bool +config MAX78000_AES + bool + config MAX78000_GCR bool diff --git a/hw/misc/max78000_aes.c b/hw/misc/max78000_aes.c new file mode 100644 index 0000000000..9de230fb07 --- /dev/null +++ b/hw/misc/max78000_aes.c @@ -0,0 +1,215 @@ +/* + * MAX78000 AES + * + * Copyright (c) 2025 Jackson Donaldson <jc...@duck.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "hw/misc/max78000_aes.h" +#include "crypto/aes.h" + +static void max78000_aes_set_status(Max78000AesState *s) +{ + s->status = 0; + if (s->result_index >= 16) { + s->status |= OUTPUT_FULL; + } + if (s->result_index == 0) { + s->status |= OUTPUT_EMPTY; + } + if (s->data_index >= 16) { + s->status |= INPUT_FULL; + } + if (s->data_index == 0) { + s->status |= INPUT_EMPTY; + } +} + +static uint64_t max78000_aes_read(void *opaque, hwaddr addr, + unsigned int size) +{ + Max78000AesState *s = opaque; + switch (addr) { + case CTRL:{ + return s->ctrl; + } + case STATUS:{ + return s->status; + } + case INTFL:{ + return s->intfl; + } + case INTEN:{ + return s->inten; + } + case FIFO:{ + if (s->result_index >= 4) { + s->intfl &= ~DONE; + s->result_index -= 4; + max78000_aes_set_status(s); + return (s->result[s->result_index] << 24) + + (s->result[s->result_index + 1] << 16) + + (s->result[s->result_index + 2] << 8) + + s->result[s->result_index + 3]; + } else{ + return 0; + } + } + default:{ + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_MAX78000_AES, __func__, addr); + break; + } + } + return 0; +} + +static void max78000_aes_do_crypto(Max78000AesState *s) +{ + int keylen = 256; + uint8_t *keydata = s->key; + if ((s->ctrl & KEY_SIZE) == 0) { + keylen = 128; + keydata += 16; + } else if ((s->ctrl & KEY_SIZE) == 1 << 6) { + keylen = 192; + keydata += 8; + } + + AES_KEY key; + if ((s->ctrl & TYPE) == 0) { + AES_set_encrypt_key(keydata, keylen, &key); + AES_set_decrypt_key(keydata, keylen, &s->internal_key); + AES_encrypt(s->data, s->result, &key); + s->result_index = 16; + } else if ((s->ctrl & TYPE) == 1 << 8) { + AES_set_decrypt_key(keydata, keylen, &key); + AES_set_decrypt_key(keydata, keylen, &s->internal_key); + AES_decrypt(s->data, s->result, &key); + s->result_index = 16; + } else{ + AES_decrypt(s->data, s->result, &s->internal_key); + s->result_index = 16; + } + s->intfl |= DONE; +} + +static void max78000_aes_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + Max78000AesState *s = opaque; + uint32_t val = val64; + int i; + switch (addr) { + case CTRL:{ + if (val & OUTPUT_FLUSH) { + s->result_index = 0; + val &= ~OUTPUT_FLUSH; + } + if (val & INPUT_FLUSH) { + s->data_index = 0; + val &= ~INPUT_FLUSH; + } + if (val & START) { + max78000_aes_do_crypto(s); + } + + /* Hardware appears to stay enabled even if 0 written */ + s->ctrl = val | (s->ctrl & AES_EN); + break; + } + + case FIFO:{ + for (i = 0; i < 4; i++) { + s->data[(12 - s->data_index) + i] = + (val >> ((3 - i) * 8)) & 0xff; + } + s->data_index += 4; + if (s->data_index >= 16) { + s->data_index = 0; + max78000_aes_do_crypto(s); + } + break; + } + case KEY_BASE ... KEY_END - 4:{ + for (i = 0; i < 4; i++) { + s->key[(KEY_END - KEY_BASE - 4) - (addr - KEY_BASE) + i] = + (val >> ((3 - i) * 8)) & 0xff; + } + break; + } + + default:{ + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_MAX78000_AES, __func__, addr); + break; + } + + } + max78000_aes_set_status(s); +} + +static void max78000_aes_reset_hold(Object *obj, ResetType type) +{ + Max78000AesState *s = MAX78000_AES(obj); + s->ctrl = 0; + s->status = 0; + s->intfl = 0; + s->inten = 0; + + s->data_index = 0; + s->result_index = 0; + + memset(s->data, 0, sizeof(s->data)); + memset(s->key, 0, sizeof(s->key)); + memset(s->result, 0, sizeof(s->result)); + memset(&s->internal_key, 0, sizeof(s->internal_key)); + + +} + +static const MemoryRegionOps max78000_aes_ops = { + .read = max78000_aes_read, + .write = max78000_aes_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void max78000_aes_init(Object *obj) +{ + Max78000AesState *s = MAX78000_AES(obj); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + memory_region_init_io(&s->mmio, obj, &max78000_aes_ops, s, + TYPE_MAX78000_AES, 0xc00); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + +} + +static void max78000_aes_class_init(ObjectClass *klass, const void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = max78000_aes_reset_hold; + +} + +static const TypeInfo max78000_aes_info = { + .name = TYPE_MAX78000_AES, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Max78000AesState), + .instance_init = max78000_aes_init, + .class_init = max78000_aes_class_init, +}; + +static void max78000_aes_register_types(void) +{ + type_register_static(&max78000_aes_info); +} + +type_init(max78000_aes_register_types) diff --git a/hw/misc/max78000_gcr.c b/hw/misc/max78000_gcr.c index a469d7a489..c8e2279b00 100644 --- a/hw/misc/max78000_gcr.c +++ b/hw/misc/max78000_gcr.c @@ -142,6 +142,8 @@ static void max78000_gcr_write(void *opaque, hwaddr addr, RTC_RESET | I2C0_RESET | SPI1_RESET | TMR3_RESET | TMR2_RESET | TMR1_RESET | TMR0_RESET | WDT0_RESET | DMA_RESET; + + max78000_gcr_reset_device("aes"); } if (val & SOFT_RESET) { /* Soft reset also resets GPIO */ @@ -151,6 +153,7 @@ static void max78000_gcr_write(void *opaque, hwaddr addr, TMR3_RESET | TMR2_RESET | TMR1_RESET | TMR0_RESET | GPIO1_RESET | GPIO0_RESET | DMA_RESET; + max78000_gcr_reset_device("aes"); } if (val & UART2_RESET) { max78000_gcr_reset_device("uart2"); @@ -164,6 +167,7 @@ static void max78000_gcr_write(void *opaque, hwaddr addr, if (val & TRNG_RESET) { max78000_gcr_reset_device("trng"); } + /* TODO: As other devices are implemented, add them here */ break; } @@ -208,7 +212,10 @@ static void max78000_gcr_write(void *opaque, hwaddr addr, } case RST1:{ /* TODO: As other devices are implemented, add them here */ - s->rst1 = val; + if (val & AES_RESET) { + max78000_gcr_reset_device("aes"); + } + break; } case PCKDIS1:{ diff --git a/hw/misc/meson.build b/hw/misc/meson.build index c7c57d924b..b1d8d8e5d2 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -70,6 +70,7 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files( 'imx_ccm.c', 'imx_rngc.c', )) +system_ss.add(when: 'CONFIG_MAX78000_AES', if_true: files('max78000_aes.c')) system_ss.add(when: 'CONFIG_MAX78000_GCR', if_true: files('max78000_gcr.c')) system_ss.add(when: 'CONFIG_MAX78000_ICC', if_true: files('max78000_icc.c')) system_ss.add(when: 'CONFIG_MAX78000_TRNG', if_true: files('max78000_trng.c')) diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h index 936825399c..69284abf9f 100644 --- a/include/hw/arm/max78000_soc.h +++ b/include/hw/arm/max78000_soc.h @@ -11,6 +11,7 @@ #include "hw/or-irq.h" #include "hw/arm/armv7m.h" +#include "hw/misc/max78000_aes.h" #include "hw/misc/max78000_gcr.h" #include "hw/misc/max78000_icc.h" #include "hw/char/max78000_uart.h" @@ -41,6 +42,7 @@ struct MAX78000State { Max78000IccState icc[MAX78000_NUM_ICC]; Max78000UartState uart[MAX78000_NUM_UART]; Max78000TrngState trng; + Max78000AesState aes; Clock *sysclk; Clock *refclk; diff --git a/include/hw/misc/max78000_aes.h b/include/hw/misc/max78000_aes.h new file mode 100644 index 0000000000..407c45ef61 --- /dev/null +++ b/include/hw/misc/max78000_aes.h @@ -0,0 +1,68 @@ +/* + * MAX78000 AES + * + * Copyright (c) 2025 Jackson Donaldson <jc...@duck.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_MAX78000_AES_H +#define HW_MAX78000_AES_H + +#include "hw/sysbus.h" +#include "crypto/aes.h" +#include "qom/object.h" + +#define TYPE_MAX78000_AES "max78000-aes" +OBJECT_DECLARE_SIMPLE_TYPE(Max78000AesState, MAX78000_AES) + +#define CTRL 0 +#define STATUS 4 +#define INTFL 8 +#define INTEN 0xc +#define FIFO 0x10 + +#define KEY_BASE 0x400 +#define KEY_END 0x420 + +/* CTRL */ +#define TYPE (1 << 9 | 1 << 8) +#define KEY_SIZE (1 << 7 | 1 << 6) +#define OUTPUT_FLUSH (1 << 5) +#define INPUT_FLUSH (1 << 4) +#define START (1 << 3) + +#define AES_EN (1 << 0) + +/* STATUS */ +#define OUTPUT_FULL (1 << 4) +#define OUTPUT_EMPTY (1 << 3) +#define INPUT_FULL (1 << 2) +#define INPUT_EMPTY (1 << 1) +#define BUSY (1 << 0) + +/* INTFL*/ +#define DONE (1 << 0) + +struct Max78000AesState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t ctrl; + uint32_t status; + uint32_t intfl; + uint32_t inten; + uint32_t data_index; + uint8_t data[16]; + + uint8_t key[32]; + AES_KEY internal_key; + + uint32_t result_index; + uint8_t result[16]; + + + qemu_irq irq; +}; + +#endif diff --git a/include/hw/misc/max78000_gcr.h b/include/hw/misc/max78000_gcr.h index 128f9b8a99..b6abaa25bc 100644 --- a/include/hw/misc/max78000_gcr.h +++ b/include/hw/misc/max78000_gcr.h @@ -88,6 +88,25 @@ OBJECT_DECLARE_SIMPLE_TYPE(Max78000GcrState, MAX78000_GCR) #define I2C1_RESET (1 << 0) +/* RST1 */ +#define CPU1_RESET (1 << 31) + +#define SIMO_RESET (1 << 25) +#define DVS_RESET (1 << 24) + +#define I2C2_RESET (1 << 20) +#define I2S_RESET (1 << 19) + +#define SMPHR_RESET (1 << 16) + +#define SPI0_RESET (1 << 11) +#define AES_RESET (1 << 10) +#define CRC_RESET (1 << 9) + +#define PT_RESET (1 << 1) +#define I2C1_RESET (1 << 0) + + #define SYSRAM0_START 0x20000000 #define SYSRAM1_START 0x20008000 #define SYSRAM2_START 0x20010000 -- 2.34.1