qemu/hw/misc/max78000_aes.c
Jackson Donaldson 30dbcd9283 hw/misc/max78000_aes: Comment Internal Key Storage
Coverity Scan noted an unusual pattern in the
MAX78000 aes device, with duplicated calls to
set_decrypt. This commit adds a comment noting
why the implementation is correct.

Signed-off-by: Jackson Donaldson <jcksn@duck.com>
Message-id: 20250716002622.84685-1-jcksn@duck.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2025-07-21 10:07:53 +01:00

230 lines
6.0 KiB
C

/*
* MAX78000 AES
*
* Copyright (c) 2025 Jackson Donaldson <jcksn@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 ldl_be_p(&s->result[s->result_index]);
} else{
return 0;
}
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"
HWADDR_PRIx "\n", __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;
}
/*
* The MAX78000 AES engine stores an internal key, which it uses only
* for decryption. This results in the slighly odd looking pairs of
* set_encrypt and set_decrypt calls below; s->internal_key is
* being stored for later use in both cases.
*/
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;
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:
assert(s->data_index <= 12);
stl_be_p(&s->data[12 - s->data_index], val);
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:
stl_be_p(&s->key[(KEY_END - KEY_BASE - 4) - (addr - KEY_BASE)], val);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"
HWADDR_PRIx "\n", __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_LITTLE_ENDIAN,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
};
static const VMStateDescription vmstate_max78000_aes = {
.name = TYPE_MAX78000_AES,
.version_id = 1,
.minimum_version_id = 1,
.fields = (const VMStateField[]) {
VMSTATE_UINT32(ctrl, Max78000AesState),
VMSTATE_UINT32(status, Max78000AesState),
VMSTATE_UINT32(intfl, Max78000AesState),
VMSTATE_UINT32(inten, Max78000AesState),
VMSTATE_UINT8_ARRAY(data, Max78000AesState, 16),
VMSTATE_UINT8_ARRAY(key, Max78000AesState, 32),
VMSTATE_UINT8_ARRAY(result, Max78000AesState, 16),
VMSTATE_UINT32_ARRAY(internal_key.rd_key, Max78000AesState, 60),
VMSTATE_INT32(internal_key.rounds, Max78000AesState),
VMSTATE_END_OF_LIST()
}
};
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);
DeviceClass *dc = DEVICE_CLASS(klass);
rc->phases.hold = max78000_aes_reset_hold;
dc->vmsd = &vmstate_max78000_aes;
}
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)