133dfff7eSJackson Donaldson /*
233dfff7eSJackson Donaldson * MAX78000 AES
333dfff7eSJackson Donaldson *
433dfff7eSJackson Donaldson * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
533dfff7eSJackson Donaldson *
633dfff7eSJackson Donaldson * SPDX-License-Identifier: GPL-2.0-or-later
733dfff7eSJackson Donaldson */
833dfff7eSJackson Donaldson
933dfff7eSJackson Donaldson #include "qemu/osdep.h"
1033dfff7eSJackson Donaldson #include "qemu/log.h"
1133dfff7eSJackson Donaldson #include "trace.h"
1233dfff7eSJackson Donaldson #include "hw/irq.h"
1333dfff7eSJackson Donaldson #include "migration/vmstate.h"
1433dfff7eSJackson Donaldson #include "hw/misc/max78000_aes.h"
1533dfff7eSJackson Donaldson #include "crypto/aes.h"
1633dfff7eSJackson Donaldson
max78000_aes_set_status(Max78000AesState * s)1733dfff7eSJackson Donaldson static void max78000_aes_set_status(Max78000AesState *s)
1833dfff7eSJackson Donaldson {
1933dfff7eSJackson Donaldson s->status = 0;
2033dfff7eSJackson Donaldson if (s->result_index >= 16) {
2133dfff7eSJackson Donaldson s->status |= OUTPUT_FULL;
2233dfff7eSJackson Donaldson }
2333dfff7eSJackson Donaldson if (s->result_index == 0) {
2433dfff7eSJackson Donaldson s->status |= OUTPUT_EMPTY;
2533dfff7eSJackson Donaldson }
2633dfff7eSJackson Donaldson if (s->data_index >= 16) {
2733dfff7eSJackson Donaldson s->status |= INPUT_FULL;
2833dfff7eSJackson Donaldson }
2933dfff7eSJackson Donaldson if (s->data_index == 0) {
3033dfff7eSJackson Donaldson s->status |= INPUT_EMPTY;
3133dfff7eSJackson Donaldson }
3233dfff7eSJackson Donaldson }
3333dfff7eSJackson Donaldson
max78000_aes_read(void * opaque,hwaddr addr,unsigned int size)3433dfff7eSJackson Donaldson static uint64_t max78000_aes_read(void *opaque, hwaddr addr,
3533dfff7eSJackson Donaldson unsigned int size)
3633dfff7eSJackson Donaldson {
3733dfff7eSJackson Donaldson Max78000AesState *s = opaque;
3833dfff7eSJackson Donaldson switch (addr) {
3933dfff7eSJackson Donaldson case CTRL:
4033dfff7eSJackson Donaldson return s->ctrl;
4133dfff7eSJackson Donaldson
4233dfff7eSJackson Donaldson case STATUS:
4333dfff7eSJackson Donaldson return s->status;
4433dfff7eSJackson Donaldson
4533dfff7eSJackson Donaldson case INTFL:
4633dfff7eSJackson Donaldson return s->intfl;
4733dfff7eSJackson Donaldson
4833dfff7eSJackson Donaldson case INTEN:
4933dfff7eSJackson Donaldson return s->inten;
5033dfff7eSJackson Donaldson
5133dfff7eSJackson Donaldson case FIFO:
5233dfff7eSJackson Donaldson if (s->result_index >= 4) {
5333dfff7eSJackson Donaldson s->intfl &= ~DONE;
5433dfff7eSJackson Donaldson s->result_index -= 4;
5533dfff7eSJackson Donaldson max78000_aes_set_status(s);
5633dfff7eSJackson Donaldson return ldl_be_p(&s->result[s->result_index]);
5733dfff7eSJackson Donaldson } else{
5833dfff7eSJackson Donaldson return 0;
5933dfff7eSJackson Donaldson }
6033dfff7eSJackson Donaldson
6133dfff7eSJackson Donaldson default:
6233dfff7eSJackson Donaldson qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"
6333dfff7eSJackson Donaldson HWADDR_PRIx "\n", __func__, addr);
6433dfff7eSJackson Donaldson break;
6533dfff7eSJackson Donaldson
6633dfff7eSJackson Donaldson }
6733dfff7eSJackson Donaldson return 0;
6833dfff7eSJackson Donaldson }
6933dfff7eSJackson Donaldson
max78000_aes_do_crypto(Max78000AesState * s)7033dfff7eSJackson Donaldson static void max78000_aes_do_crypto(Max78000AesState *s)
7133dfff7eSJackson Donaldson {
7233dfff7eSJackson Donaldson int keylen = 256;
7333dfff7eSJackson Donaldson uint8_t *keydata = s->key;
7433dfff7eSJackson Donaldson if ((s->ctrl & KEY_SIZE) == 0) {
7533dfff7eSJackson Donaldson keylen = 128;
7633dfff7eSJackson Donaldson keydata += 16;
7733dfff7eSJackson Donaldson } else if ((s->ctrl & KEY_SIZE) == 1 << 6) {
7833dfff7eSJackson Donaldson keylen = 192;
7933dfff7eSJackson Donaldson keydata += 8;
8033dfff7eSJackson Donaldson }
8133dfff7eSJackson Donaldson
82*30dbcd92SJackson Donaldson /*
83*30dbcd92SJackson Donaldson * The MAX78000 AES engine stores an internal key, which it uses only
84*30dbcd92SJackson Donaldson * for decryption. This results in the slighly odd looking pairs of
85*30dbcd92SJackson Donaldson * set_encrypt and set_decrypt calls below; s->internal_key is
86*30dbcd92SJackson Donaldson * being stored for later use in both cases.
87*30dbcd92SJackson Donaldson */
8833dfff7eSJackson Donaldson AES_KEY key;
8933dfff7eSJackson Donaldson if ((s->ctrl & TYPE) == 0) {
9033dfff7eSJackson Donaldson AES_set_encrypt_key(keydata, keylen, &key);
9133dfff7eSJackson Donaldson AES_set_decrypt_key(keydata, keylen, &s->internal_key);
9233dfff7eSJackson Donaldson AES_encrypt(s->data, s->result, &key);
9333dfff7eSJackson Donaldson s->result_index = 16;
9433dfff7eSJackson Donaldson } else if ((s->ctrl & TYPE) == 1 << 8) {
9533dfff7eSJackson Donaldson AES_set_decrypt_key(keydata, keylen, &key);
9633dfff7eSJackson Donaldson AES_set_decrypt_key(keydata, keylen, &s->internal_key);
9733dfff7eSJackson Donaldson AES_decrypt(s->data, s->result, &key);
9833dfff7eSJackson Donaldson s->result_index = 16;
9933dfff7eSJackson Donaldson } else{
10033dfff7eSJackson Donaldson AES_decrypt(s->data, s->result, &s->internal_key);
10133dfff7eSJackson Donaldson s->result_index = 16;
10233dfff7eSJackson Donaldson }
10333dfff7eSJackson Donaldson s->intfl |= DONE;
10433dfff7eSJackson Donaldson }
10533dfff7eSJackson Donaldson
max78000_aes_write(void * opaque,hwaddr addr,uint64_t val64,unsigned int size)10633dfff7eSJackson Donaldson static void max78000_aes_write(void *opaque, hwaddr addr,
10733dfff7eSJackson Donaldson uint64_t val64, unsigned int size)
10833dfff7eSJackson Donaldson {
10933dfff7eSJackson Donaldson Max78000AesState *s = opaque;
11033dfff7eSJackson Donaldson uint32_t val = val64;
11133dfff7eSJackson Donaldson switch (addr) {
11233dfff7eSJackson Donaldson case CTRL:
11333dfff7eSJackson Donaldson if (val & OUTPUT_FLUSH) {
11433dfff7eSJackson Donaldson s->result_index = 0;
11533dfff7eSJackson Donaldson val &= ~OUTPUT_FLUSH;
11633dfff7eSJackson Donaldson }
11733dfff7eSJackson Donaldson if (val & INPUT_FLUSH) {
11833dfff7eSJackson Donaldson s->data_index = 0;
11933dfff7eSJackson Donaldson val &= ~INPUT_FLUSH;
12033dfff7eSJackson Donaldson }
12133dfff7eSJackson Donaldson if (val & START) {
12233dfff7eSJackson Donaldson max78000_aes_do_crypto(s);
12333dfff7eSJackson Donaldson }
12433dfff7eSJackson Donaldson
12533dfff7eSJackson Donaldson /* Hardware appears to stay enabled even if 0 written */
12633dfff7eSJackson Donaldson s->ctrl = val | (s->ctrl & AES_EN);
12733dfff7eSJackson Donaldson break;
12833dfff7eSJackson Donaldson
12933dfff7eSJackson Donaldson case FIFO:
13033dfff7eSJackson Donaldson assert(s->data_index <= 12);
13133dfff7eSJackson Donaldson stl_be_p(&s->data[12 - s->data_index], val);
13233dfff7eSJackson Donaldson s->data_index += 4;
13333dfff7eSJackson Donaldson if (s->data_index >= 16) {
13433dfff7eSJackson Donaldson s->data_index = 0;
13533dfff7eSJackson Donaldson max78000_aes_do_crypto(s);
13633dfff7eSJackson Donaldson }
13733dfff7eSJackson Donaldson break;
13833dfff7eSJackson Donaldson
13933dfff7eSJackson Donaldson case KEY_BASE ... KEY_END - 4:
14033dfff7eSJackson Donaldson stl_be_p(&s->key[(KEY_END - KEY_BASE - 4) - (addr - KEY_BASE)], val);
14133dfff7eSJackson Donaldson break;
14233dfff7eSJackson Donaldson
14333dfff7eSJackson Donaldson default:
14433dfff7eSJackson Donaldson qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"
14533dfff7eSJackson Donaldson HWADDR_PRIx "\n", __func__, addr);
14633dfff7eSJackson Donaldson break;
14733dfff7eSJackson Donaldson
14833dfff7eSJackson Donaldson }
14933dfff7eSJackson Donaldson max78000_aes_set_status(s);
15033dfff7eSJackson Donaldson }
15133dfff7eSJackson Donaldson
max78000_aes_reset_hold(Object * obj,ResetType type)15233dfff7eSJackson Donaldson static void max78000_aes_reset_hold(Object *obj, ResetType type)
15333dfff7eSJackson Donaldson {
15433dfff7eSJackson Donaldson Max78000AesState *s = MAX78000_AES(obj);
15533dfff7eSJackson Donaldson s->ctrl = 0;
15633dfff7eSJackson Donaldson s->status = 0;
15733dfff7eSJackson Donaldson s->intfl = 0;
15833dfff7eSJackson Donaldson s->inten = 0;
15933dfff7eSJackson Donaldson
16033dfff7eSJackson Donaldson s->data_index = 0;
16133dfff7eSJackson Donaldson s->result_index = 0;
16233dfff7eSJackson Donaldson
16333dfff7eSJackson Donaldson memset(s->data, 0, sizeof(s->data));
16433dfff7eSJackson Donaldson memset(s->key, 0, sizeof(s->key));
16533dfff7eSJackson Donaldson memset(s->result, 0, sizeof(s->result));
16633dfff7eSJackson Donaldson memset(&s->internal_key, 0, sizeof(s->internal_key));
16733dfff7eSJackson Donaldson }
16833dfff7eSJackson Donaldson
16933dfff7eSJackson Donaldson static const MemoryRegionOps max78000_aes_ops = {
17033dfff7eSJackson Donaldson .read = max78000_aes_read,
17133dfff7eSJackson Donaldson .write = max78000_aes_write,
17233dfff7eSJackson Donaldson .endianness = DEVICE_LITTLE_ENDIAN,
17333dfff7eSJackson Donaldson .valid.min_access_size = 4,
17433dfff7eSJackson Donaldson .valid.max_access_size = 4,
17533dfff7eSJackson Donaldson };
17633dfff7eSJackson Donaldson
17733dfff7eSJackson Donaldson static const VMStateDescription vmstate_max78000_aes = {
17833dfff7eSJackson Donaldson .name = TYPE_MAX78000_AES,
17933dfff7eSJackson Donaldson .version_id = 1,
18033dfff7eSJackson Donaldson .minimum_version_id = 1,
18133dfff7eSJackson Donaldson .fields = (const VMStateField[]) {
18233dfff7eSJackson Donaldson VMSTATE_UINT32(ctrl, Max78000AesState),
18333dfff7eSJackson Donaldson VMSTATE_UINT32(status, Max78000AesState),
18433dfff7eSJackson Donaldson VMSTATE_UINT32(intfl, Max78000AesState),
18533dfff7eSJackson Donaldson VMSTATE_UINT32(inten, Max78000AesState),
18633dfff7eSJackson Donaldson VMSTATE_UINT8_ARRAY(data, Max78000AesState, 16),
18733dfff7eSJackson Donaldson VMSTATE_UINT8_ARRAY(key, Max78000AesState, 32),
18833dfff7eSJackson Donaldson VMSTATE_UINT8_ARRAY(result, Max78000AesState, 16),
18933dfff7eSJackson Donaldson VMSTATE_UINT32_ARRAY(internal_key.rd_key, Max78000AesState, 60),
19033dfff7eSJackson Donaldson VMSTATE_INT32(internal_key.rounds, Max78000AesState),
19133dfff7eSJackson Donaldson VMSTATE_END_OF_LIST()
19233dfff7eSJackson Donaldson }
19333dfff7eSJackson Donaldson };
19433dfff7eSJackson Donaldson
max78000_aes_init(Object * obj)19533dfff7eSJackson Donaldson static void max78000_aes_init(Object *obj)
19633dfff7eSJackson Donaldson {
19733dfff7eSJackson Donaldson Max78000AesState *s = MAX78000_AES(obj);
19833dfff7eSJackson Donaldson sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
19933dfff7eSJackson Donaldson
20033dfff7eSJackson Donaldson memory_region_init_io(&s->mmio, obj, &max78000_aes_ops, s,
20133dfff7eSJackson Donaldson TYPE_MAX78000_AES, 0xc00);
20233dfff7eSJackson Donaldson sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
20333dfff7eSJackson Donaldson
20433dfff7eSJackson Donaldson }
20533dfff7eSJackson Donaldson
max78000_aes_class_init(ObjectClass * klass,const void * data)20633dfff7eSJackson Donaldson static void max78000_aes_class_init(ObjectClass *klass, const void *data)
20733dfff7eSJackson Donaldson {
20833dfff7eSJackson Donaldson ResettableClass *rc = RESETTABLE_CLASS(klass);
20933dfff7eSJackson Donaldson DeviceClass *dc = DEVICE_CLASS(klass);
21033dfff7eSJackson Donaldson
21133dfff7eSJackson Donaldson rc->phases.hold = max78000_aes_reset_hold;
21233dfff7eSJackson Donaldson dc->vmsd = &vmstate_max78000_aes;
21333dfff7eSJackson Donaldson
21433dfff7eSJackson Donaldson }
21533dfff7eSJackson Donaldson
21633dfff7eSJackson Donaldson static const TypeInfo max78000_aes_info = {
21733dfff7eSJackson Donaldson .name = TYPE_MAX78000_AES,
21833dfff7eSJackson Donaldson .parent = TYPE_SYS_BUS_DEVICE,
21933dfff7eSJackson Donaldson .instance_size = sizeof(Max78000AesState),
22033dfff7eSJackson Donaldson .instance_init = max78000_aes_init,
22133dfff7eSJackson Donaldson .class_init = max78000_aes_class_init,
22233dfff7eSJackson Donaldson };
22333dfff7eSJackson Donaldson
max78000_aes_register_types(void)22433dfff7eSJackson Donaldson static void max78000_aes_register_types(void)
22533dfff7eSJackson Donaldson {
22633dfff7eSJackson Donaldson type_register_static(&max78000_aes_info);
22733dfff7eSJackson Donaldson }
22833dfff7eSJackson Donaldson
22933dfff7eSJackson Donaldson type_init(max78000_aes_register_types)
230