From a00a687710957cfaa87af33ca9846a4857a04cf6 Mon Sep 17 00:00:00 2001 From: Omar Chebib Date: Thu, 15 Jun 2023 14:54:22 +0800 Subject: [PATCH] hw/misc: implement AES-DMA working mode for ESP32-C3 --- hw/misc/esp32c3_aes.c | 249 +++++++++++++++++++++++++++++++--- hw/misc/meson.build | 6 +- hw/riscv/esp32c3.c | 17 ++- include/hw/misc/esp32c3_aes.h | 20 ++- 4 files changed, 265 insertions(+), 27 deletions(-) diff --git a/hw/misc/esp32c3_aes.c b/hw/misc/esp32c3_aes.c index 26cd50cce3ed..90a1b7f1ebe6 100644 --- a/hw/misc/esp32c3_aes.c +++ b/hw/misc/esp32c3_aes.c @@ -10,8 +10,11 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/misc/esp32c3_aes.h" -#include "crypto/aes.h" #include "qemu/error-report.h" +#include +#include +#include "hw/irq.h" +#include "crypto/aes.h" #define AES_WARNING 0 #define AES_DEBUG 0 @@ -19,21 +22,197 @@ static void esp32c3_aes_dma_exit(ESP32C3AesState *s) { - /* DMA is not supported yet */ - (void) s; + s->state_reg = ESP32C3_AES_IDLE; } +static void* esp32c3_aes_get_buffer(uint32_t size) +{ + /* Instead of reallocating a buffer every time, keep a watermark and a single buffer */ + static void* buffer = NULL; + static uint32_t buf_size = 0; -static void esp32c3_aes_start(ESP32C3AesState *s) + if (buf_size < size) { + buffer = g_realloc(buffer, size); + buf_size = size; + } + + return buffer; +} + + +/** + * @brief Interpret data in IV memory as a counter and add a block count to its value. + * Used in CTR block mode. + */ +static void esp32c3_aes_ctr_add_counter(ESP32C3AesState *s, uint32_t blocks) { - AES_KEY aes_key; + /* Check the length of this counter in bits. In both cases, it is stored in BIG-ENDIAN */ + if (FIELD_EX32(s->inc_sel_reg, AES_INC_SEL_REG, AES_INC_SEL) == 1) { + /* 128-bit mode, no native 128 integer type, use two 64-bit types */ + uint64_t* low_ptr = (uint64_t*) (s->iv_mem + sizeof(uint64_t)); + uint64_t* high_ptr = (uint64_t*) s->iv_mem; + const uint64_t original = be64toh(*low_ptr); + uint64_t value = original + blocks; + *low_ptr = htobe64(value); + /* If the value overflowed, we have to update the upper part too */ + if (original > value) { + value = be64toh(*high_ptr) + 1; + *high_ptr = htobe64(value); + } + } else { + /* 32-bit mode */ + uint32_t* counter_ptr = (uint32_t*) &s->iv_mem[ESP32C3_AES_IV_REG_CNT - sizeof(uint32_t)]; + const uint32_t value = be32toh(*counter_ptr) + blocks; + *counter_ptr = htobe32(value); + } +} + - /* DMA mode is not supported yet! */ - if (FIELD_EX32(s->dma_enable_reg , AES_DMA_ENA_REG, AES_DMA_ENA) != 0) { - error_report("[AES] DMA-AES is not supported yet\n"); +static void esp32c3_aes_dma_start(ESP32C3AesState *s) +{ + gcry_cipher_hd_t ghandle; + uint32_t gdma_out_idx; + uint32_t gdma_in_idx; + + const enum gcry_cipher_modes cipher_map[ESP32C3_AES_CIPHER_COUNT] = { + [ESP32C3_AES_ECB_CIPHER] = GCRY_CIPHER_MODE_ECB, + [ESP32C3_AES_CBC_CIPHER] = GCRY_CIPHER_MODE_CBC, + [ESP32C3_AES_OFB_CIPHER] = GCRY_CIPHER_MODE_OFB, + [ESP32C3_AES_CTR_CIPHER] = GCRY_CIPHER_MODE_CTR, + [ESP32C3_AES_CFB8_CIPHER] = GCRY_CIPHER_MODE_CFB8, + [ESP32C3_AES_CFB128_CIPHER] = GCRY_CIPHER_MODE_CFB, + }; + + /* Get the block Cipher mode */ + const uint32_t cipher_mode = FIELD_EX32(s->block_mode_reg , AES_BLK_MODE_REG, AES_BLOCK_MODE); + + /* Check whether we have to encrypt or decrypt */ + const uint32_t mode = FIELD_EX32(s->mode_reg , AES_MODE_REG, AES_MODE); + const bool encrypt = (mode == ESP32C3_AES_MODE_128_ENC) || (mode == ESP32C3_AES_MODE_256_ENC); + const bool decrypt = (mode == ESP32C3_AES_MODE_128_DEC) || (mode == ESP32C3_AES_MODE_256_DEC); + + /* Get the length, in bits of the key */ + const int length = (mode == ESP32C3_AES_MODE_128_ENC || mode == ESP32C3_AES_MODE_128_DEC) ? 128 : 256; + const int algo = length == 128 ? GCRY_CIPHER_AES128 : GCRY_CIPHER_AES256; + + if (cipher_mode >= ESP32C3_AES_CIPHER_COUNT) { + error_report("[AES] Invalid or unsupported Cipher block mode!"); return; + } else if (!decrypt && !encrypt) { + error_report("[AES] Invalid mode!"); + return; + } + + gcry_error_t err = gcry_cipher_open(&ghandle, algo, cipher_map[cipher_mode], 0); + if (err) { + error_report("[AES] error 0x%x when opening cipher", err); + return; + } + + /* Cast the keys and data to byte array. + * This can only work as-is if the host computer is has a little-endian CPU. */ + const uint8_t* key = (uint8_t*) &s->key; + uint8_t* iv_mem = (uint8_t*) &s->iv_mem; + + /* Set the algorithm key */ + err = gcry_cipher_setkey(ghandle, key, length / 8); + if (err) { + error_report("[AES] error 0x%x setting key", err); + goto close_exit; + } + + /* `iv_mem` field represents the Initialization Vector for CBC/OFB/CFB operations + * But it represents the Initial Counter Block for CTR operation. + * It shall be ignored for ECB block operation. */ + if (cipher_mode == ESP32C3_AES_CTR_CIPHER) { + err = gcry_cipher_setctr(ghandle, iv_mem, ESP32C3_AES_IV_REG_CNT); + } else if (cipher_mode != ESP32C3_AES_ECB_CIPHER) { + err = gcry_cipher_setiv(ghandle, iv_mem, ESP32C3_AES_IV_REG_CNT); + } + + if (err) { + error_report("[AES] error 0x%x setting IV memory", err); + goto close_exit; + } + + /* Get the GDMA input channel index for AES peripheral */ + assert(s->gdma != NULL); + + if ( !esp32c3_gdma_get_channel_periph(s->gdma, GDMA_AES, ESP32C3_GDMA_OUT_IDX, &gdma_out_idx) || + !esp32c3_gdma_get_channel_periph(s->gdma, GDMA_AES, ESP32C3_GDMA_IN_IDX, &gdma_in_idx) ) { + warn_report("[AES] GDMA requested but no properly configured channel found"); + goto close_exit; + } + + /* Block number represents the number of 128-bit (16-byte) blocks to encrypt. + * If block_num_reg is 100, we have to encrypt 100*128/8 = 1600 bytes */ + uint32_t buf_size = s->block_num_reg * 16; + uint8_t* buffer = esp32c3_aes_get_buffer(buf_size); + + if ( !esp32c3_gdma_read_channel(s->gdma, gdma_out_idx, buffer, buf_size) ) { + warn_report("[AES] Error reading from GDMA buffer"); + goto close_exit; } + /* Reading was successful, process the buffer (encrypt/decrypt) and write back to the GDMA OUT buffer */ + if (encrypt) { + err = gcry_cipher_encrypt(ghandle, buffer, buf_size, NULL, 0); + + if (cipher_mode != ESP32C3_AES_CTR_CIPHER) { + /* On the real hardware, IV memory is used in-place for encrypting data, so copy the last encrypted block to IV memory */ + memcpy(iv_mem, buffer + buf_size - 16, ESP32C3_AES_IV_REG_CNT); + } + } else { + /* Store the last block of plaintext, needed for OFB */ + const uint8_t* buffer_last_block = buffer + buf_size - ESP32C3_AES_IV_REG_CNT; + uint8_t plaintext[ESP32C3_AES_IV_REG_CNT]; + memcpy(plaintext, buffer_last_block, ESP32C3_AES_IV_REG_CNT); + + /* The IV memory is initalized with the encrypted data, so do the copy now */ + if (cipher_mode != ESP32C3_AES_OFB_CIPHER && cipher_mode != ESP32C3_AES_CTR_CIPHER) { + memcpy(iv_mem, buffer + buf_size - 16, ESP32C3_AES_IV_REG_CNT); + } + + err = gcry_cipher_decrypt(ghandle, buffer, buf_size, NULL, 0); + + /* For OFB, it is done after the decryption. Moreover, the hardware XOR the original plaintext with the output + * and stores the result in IV memory. */ + if (cipher_mode == ESP32C3_AES_OFB_CIPHER) { + for (int i = 0; i < ESP32C3_AES_IV_REG_CNT; i++) { + iv_mem[i] = buffer_last_block[i] ^ plaintext[i]; + } + } + } + + if (cipher_mode == ESP32C3_AES_CTR_CIPHER) { + esp32c3_aes_ctr_add_counter(s, s->block_num_reg); + } + + if (err) { + error_report("[AES] error processing memory"); + goto close_exit; + } + + if ( !esp32c3_gdma_write_channel(s->gdma, gdma_in_idx, buffer, buf_size) ) { + warn_report("[AES] Error writing to GDMA buffer"); + goto close_exit; + } + + s->state_reg = ESP32C3_AES_DONE; + + if (s->int_ena_reg) { + qemu_irq_raise(s->irq); + } + +close_exit: + gcry_cipher_close(ghandle); +} + + +static void esp32c3_aes_start(ESP32C3AesState *s) +{ + AES_KEY aes_key; + /* Check whether we have to encrypt or decrypt */ const uint32_t mode = FIELD_EX32(s->mode_reg , AES_MODE_REG, AES_MODE); const bool encrypt = (mode == ESP32C3_AES_MODE_128_ENC) || (mode == ESP32C3_AES_MODE_256_ENC); @@ -68,6 +247,10 @@ static uint64_t esp32c3_aes_read(void *opaque, hwaddr addr, unsigned int size) ESP32C3AesState *s = ESP32C3_AES(opaque); uint64_t r = 0; + /* At the moment, make the assumption that we always write a 32-bit word, except for IV memory */ + assert((addr >= A_AES_IV_MEM_0_REG && addr <= A_AES_IV_MEM_15_REG) || + size == sizeof(uint32_t)); + switch (addr) { case A_AES_KEY_0_REG ... A_AES_KEY_7_REG: r = s->key[(addr - A_AES_KEY_0_REG) / sizeof(uint32_t)]; @@ -82,7 +265,13 @@ static uint64_t esp32c3_aes_read(void *opaque, hwaddr addr, unsigned int size) break; case A_AES_IV_MEM_0_REG ... A_AES_IV_MEM_15_REG: - r = s->iv_mem[(addr - A_AES_IV_MEM_0_REG) / sizeof(uint32_t)]; + /* Use r as the offset */ + r = addr - A_AES_IV_MEM_0_REG; + if (size == sizeof(uint32_t)) { + r = *((uint32_t*) (s->iv_mem + r)); + } else if (size == sizeof(uint8_t)) { + r = s->iv_mem[r]; + } break; case A_AES_STATE_REG: @@ -116,13 +305,13 @@ static uint64_t esp32c3_aes_read(void *opaque, hwaddr addr, unsigned int size) default: #if AES_WARNING /* Other registers are not supported yet */ - warn_report("[AES] Unsupported read to %08lx\n", addr); + warn_report("[AES] Unsupported read to %08lx", addr); #endif break; } #if AES_DEBUG - info_report("[AES] Reading from %08lx (%08lx)\n", addr, r); + info_report("[AES] Reading from %08lx (%08lx)", addr, r); #endif @@ -134,6 +323,11 @@ static void esp32c3_aes_write(void *opaque, hwaddr addr, uint64_t value, unsigned int size) { ESP32C3AesState *s = ESP32C3_AES(opaque); + uint32_t offset = 0; + + /* At the moment, make the assumption that we always write a 32-bit word, except for IV memory */ + assert((addr >= A_AES_IV_MEM_0_REG && addr <= A_AES_IV_MEM_15_REG) || + size == sizeof(uint32_t)); switch (addr) { case A_AES_KEY_0_REG ... A_AES_KEY_7_REG: @@ -145,7 +339,12 @@ static void esp32c3_aes_write(void *opaque, hwaddr addr, break; case A_AES_IV_MEM_0_REG ... A_AES_IV_MEM_15_REG: - s->iv_mem[(addr - A_AES_IV_MEM_0_REG) / sizeof(uint32_t)] = value; + offset = addr - A_AES_IV_MEM_0_REG; + if (size == sizeof(uint32_t)) { + *((uint32_t*) (s->iv_mem + offset)) = value; + } else if (size == sizeof(uint8_t)) { + s->iv_mem[offset] = value & 0xff; + } break; case A_AES_MODE_REG: @@ -154,7 +353,12 @@ static void esp32c3_aes_write(void *opaque, hwaddr addr, case A_AES_TRIGGER_REG: if (FIELD_EX32(value, AES_TRIGGER_REG, AES_TRIGGER)) { - esp32c3_aes_start(s); + /* DMA mode is different than "regular" mode */ + if (FIELD_EX32(s->dma_enable_reg , AES_DMA_ENA_REG, AES_DMA_ENA) != 0) { + esp32c3_aes_dma_start(s); + } else { + esp32c3_aes_start(s); + } } break; @@ -176,7 +380,7 @@ static void esp32c3_aes_write(void *opaque, hwaddr addr, case A_AES_INT_CLR_REG: if (FIELD_EX32(value, AES_INT_CLR_REG, AES_INT_CLR)) { - s->int_st = 0; + qemu_irq_lower(s->irq); } break; @@ -191,13 +395,13 @@ static void esp32c3_aes_write(void *opaque, hwaddr addr, default: #if AES_WARNING /* Other registers are not supported yet */ - warn_report("[AES] Unsupported write to %08lx (%08lx)\n", addr, value); + warn_report("[AES] Unsupported write to %08lx (%08lx)", addr, value); #endif break; } #if AES_DEBUG - info_report("[AES] Writing to %08lx (%08lx)\n", addr, value); + info_report("[AES] Writing to %08lx (%08lx)", addr, value); #endif } @@ -224,6 +428,16 @@ static void esp32c3_aes_reset(DeviceState *dev) s->int_ena_reg = 0; } +static void esp32c3_aes_realize(DeviceState *dev, Error **errp) +{ + ESP32C3AesState *s = ESP32C3_AES(dev); + + /* Make sure GDMA was set of issue an error */ + if (s->gdma == NULL) { + error_report("[AES] GDMA controller must be set!"); + } +} + static void esp32c3_aes_init(Object *obj) { ESP32C3AesState *s = ESP32C3_AES(obj); @@ -232,12 +446,15 @@ static void esp32c3_aes_init(Object *obj) memory_region_init_io(&s->iomem, obj, &esp32c3_aes_ops, s, TYPE_ESP32C3_AES, ESP32C3_AES_REGS_SIZE); sysbus_init_mmio(sbd, &s->iomem); + + sysbus_init_irq(sbd, &s->irq); } static void esp32_aes_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = esp32c3_aes_realize; dc->reset = esp32c3_aes_reset; } diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 78f2c968e394..9a34c6e96e94 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -140,14 +140,16 @@ softmmu_ss.add(when: 'CONFIG_RISCV_ESP32C3', if_true: files( 'esp32c3_cache.c', 'esp32c3_sha.c', 'esp32c3_jtag.c', - 'esp32c3_rtc_cntl.c', - 'esp32c3_aes.c' + 'esp32c3_rtc_cntl.c' )) if gcrypt.found() softmmu_ss.add(when: [gcrypt, 'CONFIG_XTENSA_ESP32'], if_true: files( 'esp32_rsa.c', )) + softmmu_ss.add(when: [gcrypt, 'CONFIG_RISCV_ESP32C3'], if_true: files( + 'esp32c3_aes.c', + )) endif softmmu_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_ahb_apb_pnp.c')) diff --git a/hw/riscv/esp32c3.c b/hw/riscv/esp32c3.c index bea0f720ec20..caa426d5b13d 100644 --- a/hw/riscv/esp32c3.c +++ b/hw/riscv/esp32c3.c @@ -426,13 +426,6 @@ static void esp32c3_machine_init(MachineState *machine) memory_region_add_subregion_overlap(sys_mem, DR_REG_SHA_BASE, mr, 0); } - /* AES realization */ - { - qdev_realize(DEVICE(&ms->aes), &ms->periph_bus, &error_fatal); - MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->aes), 0); - memory_region_add_subregion_overlap(sys_mem, DR_REG_AES_BASE, mr, 0); - } - /* Timer Groups realization */ { qdev_realize(DEVICE(&ms->timg[0]), &ms->periph_bus, &error_fatal); @@ -487,6 +480,16 @@ static void esp32c3_machine_init(MachineState *machine) } + /* AES realization */ + { + ms->aes.gdma = &ms->gdma; + qdev_realize(DEVICE(&ms->aes), &ms->periph_bus, &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->aes), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_AES_BASE, mr, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(&ms->aes), 0, + qdev_get_gpio_in(intmatrix_dev, ETS_AES_INTR_SOURCE)); + } + /* Open and load the "bios", which is the ROM binary, also named "first stage bootloader" */ char *rom_binary = qemu_find_file(QEMU_FILE_TYPE_BIOS, "esp32c3-rom.bin"); if (rom_binary == NULL) { diff --git a/include/hw/misc/esp32c3_aes.h b/include/hw/misc/esp32c3_aes.h index 2a168e05007e..6469866ea9e1 100644 --- a/include/hw/misc/esp32c3_aes.h +++ b/include/hw/misc/esp32c3_aes.h @@ -12,6 +12,7 @@ #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/registerfields.h" +#include "hw/dma/esp32c3_gdma.h" #define TYPE_ESP32C3_AES "misc.esp32c3.aes" #define ESP32C3_AES(obj) OBJECT_CHECK(ESP32C3AesState, (obj), TYPE_ESP32C3_AES) @@ -35,6 +36,18 @@ #define ESP32C3_AES_MODE_256_DEC 6 +/** + * Block Cipher modes + */ +#define ESP32C3_AES_ECB_CIPHER 0 +#define ESP32C3_AES_CBC_CIPHER 1 +#define ESP32C3_AES_OFB_CIPHER 2 +#define ESP32C3_AES_CTR_CIPHER 3 +#define ESP32C3_AES_CFB8_CIPHER 4 +#define ESP32C3_AES_CFB128_CIPHER 5 +#define ESP32C3_AES_CIPHER_COUNT 6 + + typedef struct ESP32C3AesState { SysBusDevice parent_object; MemoryRegion iomem; @@ -42,7 +55,7 @@ typedef struct ESP32C3AesState { uint32_t key[ESP32C3_AES_KEY_REG_CNT]; uint32_t text_in[ESP32C3_AES_TEXT_REG_CNT]; uint32_t text_out[ESP32C3_AES_TEXT_REG_CNT]; - uint32_t iv_mem[ESP32C3_AES_IV_REG_CNT]; + uint8_t iv_mem[ESP32C3_AES_IV_REG_CNT]; uint32_t mode_reg; uint32_t state_reg; @@ -51,8 +64,11 @@ typedef struct ESP32C3AesState { uint32_t block_num_reg; uint32_t inc_sel_reg; - uint32_t int_st; uint32_t int_ena_reg; + qemu_irq irq; + + /* Public: must be set by the machine before realizing current instance */ + ESP32C3GdmaState *gdma; } ESP32C3AesState;