aboutsummaryrefslogtreecommitdiff
path: root/hw/misc
diff options
context:
space:
mode:
Diffstat (limited to 'hw/misc')
-rw-r--r--hw/misc/Kconfig13
-rw-r--r--hw/misc/aspeed_hace.c479
-rw-r--r--hw/misc/aspeed_sbc.c197
-rw-r--r--hw/misc/aspeed_scu.c22
-rw-r--r--hw/misc/aspeed_sdmc.c3
-rw-r--r--hw/misc/ivshmem-flat.c9
-rw-r--r--hw/misc/ivshmem-pci.c16
-rw-r--r--hw/misc/max78000_aes.c229
-rw-r--r--hw/misc/max78000_gcr.c351
-rw-r--r--hw/misc/max78000_icc.c120
-rw-r--r--hw/misc/max78000_trng.c139
-rw-r--r--hw/misc/meson.build4
-rw-r--r--hw/misc/stm32_rcc.c2
-rw-r--r--hw/misc/trace-events14
-rw-r--r--hw/misc/xlnx-versal-cframe-reg.c9
-rw-r--r--hw/misc/xlnx-versal-crl.c602
16 files changed, 1956 insertions, 253 deletions
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index ec0fa5a..4e35657 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -47,6 +47,18 @@ config A9SCU
config ARM11SCU
bool
+config MAX78000_AES
+ bool
+
+config MAX78000_GCR
+ bool
+
+config MAX78000_ICC
+ bool
+
+config MAX78000_TRNG
+ bool
+
config MOS6522
bool
@@ -107,6 +119,7 @@ config STM32L4X5_RCC
config MIPS_ITU
bool
+ depends on TCG
config MPS2_FPGAIO
bool
diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c
index f4bff32..726368f 100644
--- a/hw/misc/aspeed_hace.c
+++ b/hw/misc/aspeed_hace.c
@@ -10,14 +10,17 @@
*/
#include "qemu/osdep.h"
+#include "qemu/cutils.h"
#include "qemu/log.h"
#include "qemu/error-report.h"
+#include "qemu/iov.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"
+#include "trace.h"
#define R_CRYPT_CMD (0x10 / 4)
@@ -27,9 +30,12 @@
#define TAG_IRQ BIT(15)
#define R_HASH_SRC (0x20 / 4)
-#define R_HASH_DEST (0x24 / 4)
+#define R_HASH_DIGEST (0x24 / 4)
#define R_HASH_KEY_BUFF (0x28 / 4)
#define R_HASH_SRC_LEN (0x2c / 4)
+#define R_HASH_SRC_HI (0x90 / 4)
+#define R_HASH_DIGEST_HI (0x94 / 4)
+#define R_HASH_KEY_BUFF_HI (0x98 / 4)
#define R_HASH_CMD (0x30 / 4)
/* Hash algorithm selection */
@@ -84,6 +90,42 @@ static const struct {
QCRYPTO_HASH_ALGO_SHA256 },
};
+static void hace_hexdump(const char *desc, const char *buf, size_t size)
+{
+ g_autoptr(GString) str = g_string_sized_new(64);
+ size_t len;
+ size_t i;
+
+ for (i = 0; i < size; i += len) {
+ len = MIN(16, size - i);
+ g_string_truncate(str, 0);
+ qemu_hexdump_line(str, buf + i, len, 1, 4);
+ trace_aspeed_hace_hexdump(desc, i, str->str);
+ }
+}
+
+static void hace_iov_hexdump(const char *desc, const struct iovec *iov,
+ const unsigned int iov_cnt)
+{
+ size_t size = 0;
+ char *buf;
+ int i;
+
+ for (i = 0; i < iov_cnt; i++) {
+ size += iov[i].iov_len;
+ }
+
+ buf = g_malloc(size);
+
+ if (!buf) {
+ return;
+ }
+
+ iov_to_buf(iov, iov_cnt, 0, buf, size);
+ hace_hexdump(desc, buf, size);
+ g_free(buf);
+}
+
static int hash_algo_lookup(uint32_t reg)
{
int i;
@@ -142,171 +184,269 @@ static bool has_padding(AspeedHACEState *s, struct iovec *iov,
return false;
}
-static int reconstruct_iov(AspeedHACEState *s, struct iovec *iov, int id,
- uint32_t *pad_offset)
+static uint64_t hash_get_source_addr(AspeedHACEState *s)
{
- int i, iov_count;
- if (*pad_offset != 0) {
- s->iov_cache[s->iov_count].iov_base = iov[id].iov_base;
- s->iov_cache[s->iov_count].iov_len = *pad_offset;
- ++s->iov_count;
- }
- for (i = 0; i < s->iov_count; i++) {
- iov[i].iov_base = s->iov_cache[i].iov_base;
- iov[i].iov_len = s->iov_cache[i].iov_len;
+ AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s);
+ uint64_t src_addr = 0;
+
+ src_addr = deposit64(src_addr, 0, 32, s->regs[R_HASH_SRC]);
+ if (ahc->has_dma64) {
+ src_addr = deposit64(src_addr, 32, 32, s->regs[R_HASH_SRC_HI]);
}
- iov_count = s->iov_count;
- s->iov_count = 0;
- s->total_req_len = 0;
- return iov_count;
+
+ return src_addr;
}
-static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode,
- bool acc_mode)
+static int hash_prepare_direct_iov(AspeedHACEState *s, struct iovec *iov,
+ bool acc_mode, bool *acc_final_request)
{
- struct iovec iov[ASPEED_HACE_MAX_SG];
uint32_t total_msg_len;
uint32_t pad_offset;
- g_autofree uint8_t *digest_buf = NULL;
- size_t digest_len = 0;
- bool sg_acc_mode_final_request = false;
- int i;
+ uint64_t src;
void *haddr;
- Error *local_err = NULL;
+ hwaddr plen;
+ int iov_idx;
+
+ plen = s->regs[R_HASH_SRC_LEN];
+ src = hash_get_source_addr(s);
+ trace_aspeed_hace_hash_addr("src", src);
+ haddr = address_space_map(&s->dram_as, src, &plen, false,
+ MEMTXATTRS_UNSPECIFIED);
+ if (haddr == NULL) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Unable to map address, addr=0x%" HWADDR_PRIx
+ " ,plen=0x%" HWADDR_PRIx "\n",
+ __func__, src, plen);
+ return -1;
+ }
- if (acc_mode && s->hash_ctx == NULL) {
- s->hash_ctx = qcrypto_hash_new(algo, &local_err);
- if (s->hash_ctx == NULL) {
- qemu_log_mask(LOG_GUEST_ERROR, "qcrypto hash failed : %s",
- error_get_pretty(local_err));
- error_free(local_err);
- return;
+ iov[0].iov_base = haddr;
+ iov_idx = 1;
+
+ if (acc_mode) {
+ s->total_req_len += plen;
+
+ if (has_padding(s, &iov[0], plen, &total_msg_len,
+ &pad_offset)) {
+ /* Padding being present indicates the final request */
+ *acc_final_request = true;
+ iov[0].iov_len = pad_offset;
+ } else {
+ iov[0].iov_len = plen;
}
+ } else {
+ iov[0].iov_len = plen;
}
- if (sg_mode) {
- uint32_t len = 0;
-
- for (i = 0; !(len & SG_LIST_LEN_LAST); i++) {
- uint32_t addr, src;
- hwaddr plen;
+ return iov_idx;
+}
- if (i == ASPEED_HACE_MAX_SG) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "aspeed_hace: guest failed to set end of sg list marker\n");
- break;
- }
+static int hash_prepare_sg_iov(AspeedHACEState *s, struct iovec *iov,
+ bool acc_mode, bool *acc_final_request)
+{
+ uint32_t total_msg_len;
+ uint32_t pad_offset;
+ uint32_t len = 0;
+ uint32_t sg_addr;
+ uint64_t src;
+ int iov_idx;
+ hwaddr plen;
+ void *haddr;
- src = s->regs[R_HASH_SRC] + (i * SG_LIST_ENTRY_SIZE);
+ src = hash_get_source_addr(s);
+ for (iov_idx = 0; !(len & SG_LIST_LEN_LAST); iov_idx++) {
+ if (iov_idx == ASPEED_HACE_MAX_SG) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Failed to set end of sg list marker\n",
+ __func__);
+ return -1;
+ }
- len = address_space_ldl_le(&s->dram_as, src,
+ len = address_space_ldl_le(&s->dram_as, src,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ sg_addr = address_space_ldl_le(&s->dram_as, src + SG_LIST_LEN_SIZE,
MEMTXATTRS_UNSPECIFIED, NULL);
+ sg_addr &= SG_LIST_ADDR_MASK;
+ trace_aspeed_hace_hash_sg(iov_idx, src, sg_addr, len);
+ /*
+ * To maintain compatibility with older SoCs such as the AST2600,
+ * the AST2700 HW automatically set bit 34 of the 64-bit sg_addr.
+ * As a result, the firmware only needs to provide a 32-bit sg_addr
+ * containing bits [31:0]. This is sufficient for the AST2700, as
+ * it uses a DRAM offset rather than a DRAM address.
+ */
+ plen = len & SG_LIST_LEN_MASK;
+ haddr = address_space_map(&s->dram_as, sg_addr, &plen, false,
+ MEMTXATTRS_UNSPECIFIED);
- addr = address_space_ldl_le(&s->dram_as, src + SG_LIST_LEN_SIZE,
- MEMTXATTRS_UNSPECIFIED, NULL);
- addr &= SG_LIST_ADDR_MASK;
+ if (haddr == NULL) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Unable to map address, sg_addr=0x%x, "
+ "plen=0x%" HWADDR_PRIx "\n",
+ __func__, sg_addr, plen);
+ return -1;
+ }
- plen = len & SG_LIST_LEN_MASK;
- haddr = address_space_map(&s->dram_as, addr, &plen, false,
- MEMTXATTRS_UNSPECIFIED);
- if (haddr == NULL) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: qcrypto failed\n", __func__);
- return;
- }
- iov[i].iov_base = haddr;
- if (acc_mode) {
- s->total_req_len += plen;
-
- if (has_padding(s, &iov[i], plen, &total_msg_len,
- &pad_offset)) {
- /* Padding being present indicates the final request */
- sg_acc_mode_final_request = true;
- iov[i].iov_len = pad_offset;
- } else {
- iov[i].iov_len = plen;
- }
+ src += SG_LIST_ENTRY_SIZE;
+
+ iov[iov_idx].iov_base = haddr;
+ if (acc_mode) {
+ s->total_req_len += plen;
+
+ if (has_padding(s, &iov[iov_idx], plen, &total_msg_len,
+ &pad_offset)) {
+ /* Padding being present indicates the final request */
+ *acc_final_request = true;
+ iov[iov_idx].iov_len = pad_offset;
} else {
- iov[i].iov_len = plen;
+ iov[iov_idx].iov_len = plen;
}
+ } else {
+ iov[iov_idx].iov_len = plen;
}
- } else {
- hwaddr len = s->regs[R_HASH_SRC_LEN];
+ }
- haddr = address_space_map(&s->dram_as, s->regs[R_HASH_SRC],
- &len, false, MEMTXATTRS_UNSPECIFIED);
- if (haddr == NULL) {
- qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto failed\n", __func__);
+ return iov_idx;
+}
+
+static uint64_t hash_get_digest_addr(AspeedHACEState *s)
+{
+ AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s);
+ uint64_t digest_addr = 0;
+
+ digest_addr = deposit64(digest_addr, 0, 32, s->regs[R_HASH_DIGEST]);
+ if (ahc->has_dma64) {
+ digest_addr = deposit64(digest_addr, 32, 32, s->regs[R_HASH_DIGEST_HI]);
+ }
+
+ return digest_addr;
+}
+
+static void hash_write_digest_and_unmap_iov(AspeedHACEState *s,
+ struct iovec *iov,
+ int iov_idx,
+ uint8_t *digest_buf,
+ size_t digest_len)
+{
+ uint64_t digest_addr = 0;
+
+ digest_addr = hash_get_digest_addr(s);
+ trace_aspeed_hace_hash_addr("digest", digest_addr);
+ if (address_space_write(&s->dram_as, digest_addr,
+ MEMTXATTRS_UNSPECIFIED,
+ digest_buf, digest_len)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Failed to write digest to 0x%" HWADDR_PRIx "\n",
+ __func__, digest_addr);
+ }
+
+ if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) {
+ hace_hexdump("digest", (char *)digest_buf, digest_len);
+ }
+
+ for (; iov_idx > 0; iov_idx--) {
+ address_space_unmap(&s->dram_as, iov[iov_idx - 1].iov_base,
+ iov[iov_idx - 1].iov_len, false,
+ iov[iov_idx - 1].iov_len);
+ }
+}
+
+static void hash_execute_non_acc_mode(AspeedHACEState *s, int algo,
+ struct iovec *iov, int iov_idx)
+{
+ g_autofree uint8_t *digest_buf = NULL;
+ Error *local_err = NULL;
+ size_t digest_len = 0;
+
+ if (qcrypto_hash_bytesv(algo, iov, iov_idx, &digest_buf,
+ &digest_len, &local_err) < 0) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: qcrypto hash bytesv failed : %s",
+ __func__, error_get_pretty(local_err));
+ error_free(local_err);
+ return;
+ }
+
+ hash_write_digest_and_unmap_iov(s, iov, iov_idx, digest_buf, digest_len);
+}
+
+static void hash_execute_acc_mode(AspeedHACEState *s, int algo,
+ struct iovec *iov, int iov_idx,
+ bool final_request)
+{
+ g_autofree uint8_t *digest_buf = NULL;
+ Error *local_err = NULL;
+ size_t digest_len = 0;
+
+ trace_aspeed_hace_hash_execute_acc_mode(final_request);
+
+ if (s->hash_ctx == NULL) {
+ s->hash_ctx = qcrypto_hash_new(algo, &local_err);
+ if (s->hash_ctx == NULL) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto hash new failed : %s",
+ __func__, error_get_pretty(local_err));
+ error_free(local_err);
return;
}
- iov[0].iov_base = haddr;
- iov[0].iov_len = len;
- i = 1;
-
- if (s->iov_count) {
- /*
- * In aspeed sdk kernel driver, sg_mode is disabled in hash_final().
- * Thus if we received a request with sg_mode disabled, it is
- * required to check whether cache is empty. If no, we should
- * combine cached iov and the current iov.
- */
- s->total_req_len += len;
- if (has_padding(s, iov, len, &total_msg_len, &pad_offset)) {
- i = reconstruct_iov(s, iov, 0, &pad_offset);
- }
- }
}
- if (acc_mode) {
- if (qcrypto_hash_updatev(s->hash_ctx, iov, i, &local_err) < 0) {
- qemu_log_mask(LOG_GUEST_ERROR, "qcrypto hash update failed : %s",
- error_get_pretty(local_err));
+ if (qcrypto_hash_updatev(s->hash_ctx, iov, iov_idx, &local_err) < 0) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto hash updatev failed : %s",
+ __func__, error_get_pretty(local_err));
+ error_free(local_err);
+ return;
+ }
+
+ if (final_request) {
+ if (qcrypto_hash_finalize_bytes(s->hash_ctx, &digest_buf,
+ &digest_len, &local_err)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: qcrypto hash finalize bytes failed : %s",
+ __func__, error_get_pretty(local_err));
error_free(local_err);
- return;
+ local_err = NULL;
}
- if (sg_acc_mode_final_request) {
- if (qcrypto_hash_finalize_bytes(s->hash_ctx, &digest_buf,
- &digest_len, &local_err)) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "qcrypto hash finalize failed : %s",
- error_get_pretty(local_err));
- error_free(local_err);
- local_err = NULL;
- }
+ qcrypto_hash_free(s->hash_ctx);
+
+ s->hash_ctx = NULL;
+ s->total_req_len = 0;
+ }
- qcrypto_hash_free(s->hash_ctx);
+ hash_write_digest_and_unmap_iov(s, iov, iov_idx, digest_buf, digest_len);
+}
- s->hash_ctx = NULL;
- s->iov_count = 0;
- s->total_req_len = 0;
- }
- } else if (qcrypto_hash_bytesv(algo, iov, i, &digest_buf,
- &digest_len, &local_err) < 0) {
- qemu_log_mask(LOG_GUEST_ERROR, "qcrypto hash bytesv failed : %s",
- error_get_pretty(local_err));
- error_free(local_err);
- return;
+static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode,
+ bool acc_mode)
+{
+ QEMU_UNINITIALIZED struct iovec iov[ASPEED_HACE_MAX_SG];
+ bool acc_final_request = false;
+ int iov_idx = -1;
+
+ /* Prepares the iov for hashing operations based on the selected mode */
+ if (sg_mode) {
+ iov_idx = hash_prepare_sg_iov(s, iov, acc_mode, &acc_final_request);
+ } else {
+ iov_idx = hash_prepare_direct_iov(s, iov, acc_mode,
+ &acc_final_request);
}
- if (address_space_write(&s->dram_as, s->regs[R_HASH_DEST],
- MEMTXATTRS_UNSPECIFIED,
- digest_buf, digest_len)) {
+ if (iov_idx <= 0) {
qemu_log_mask(LOG_GUEST_ERROR,
- "aspeed_hace: address space write failed\n");
+ "%s: Failed to prepare iov\n", __func__);
+ return;
}
- for (; i > 0; i--) {
- address_space_unmap(&s->dram_as, iov[i - 1].iov_base,
- iov[i - 1].iov_len, false,
- iov[i - 1].iov_len);
+ if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) {
+ hace_iov_hexdump("plaintext", iov, iov_idx);
}
- /*
- * Set status bits to indicate completion. Testing shows hardware sets
- * these irrespective of HASH_IRQ_EN.
- */
- s->regs[R_STATUS] |= HASH_IRQ;
+ /* Executes the hash operation */
+ if (acc_mode) {
+ hash_execute_acc_mode(s, algo, iov, iov_idx, acc_final_request);
+ } else {
+ hash_execute_non_acc_mode(s, algo, iov, iov_idx);
+ }
}
static uint64_t aspeed_hace_read(void *opaque, hwaddr addr, unsigned int size)
@@ -315,12 +455,7 @@ static uint64_t aspeed_hace_read(void *opaque, hwaddr addr, unsigned int size)
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;
- }
+ trace_aspeed_hace_read(addr << 2, s->regs[addr]);
return s->regs[addr];
}
@@ -333,12 +468,7 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data,
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;
- }
+ trace_aspeed_hace_write(addr << 2, data);
switch (addr) {
case R_STATUS:
@@ -362,7 +492,7 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data,
case R_HASH_SRC:
data &= ahc->src_mask;
break;
- case R_HASH_DEST:
+ case R_HASH_DIGEST:
data &= ahc->dest_mask;
break;
case R_HASH_KEY_BUFF:
@@ -390,10 +520,16 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data,
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Invalid hash algorithm selection 0x%"PRIx64"\n",
__func__, data & ahc->hash_mask);
- break;
+ } else {
+ do_hash_operation(s, algo, data & HASH_SG_EN,
+ ((data & HASH_HMAC_MASK) == HASH_DIGEST_ACCUM));
}
- do_hash_operation(s, algo, data & HASH_SG_EN,
- ((data & HASH_HMAC_MASK) == HASH_DIGEST_ACCUM));
+
+ /*
+ * Set status bits to indicate completion. Testing shows hardware sets
+ * these irrespective of HASH_IRQ_EN.
+ */
+ s->regs[R_STATUS] |= HASH_IRQ;
if (data & HASH_IRQ_EN) {
qemu_irq_raise(s->irq);
@@ -410,6 +546,15 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data,
}
}
break;
+ case R_HASH_SRC_HI:
+ data &= ahc->src_hi_mask;
+ break;
+ case R_HASH_DIGEST_HI:
+ data &= ahc->dest_hi_mask;
+ break;
+ case R_HASH_KEY_BUFF_HI:
+ data &= ahc->key_hi_mask;
+ break;
default:
break;
}
@@ -430,14 +575,14 @@ static const MemoryRegionOps aspeed_hace_ops = {
static void aspeed_hace_reset(DeviceState *dev)
{
struct AspeedHACEState *s = ASPEED_HACE(dev);
+ AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s);
if (s->hash_ctx != NULL) {
qcrypto_hash_free(s->hash_ctx);
s->hash_ctx = NULL;
}
- memset(s->regs, 0, sizeof(s->regs));
- s->iov_count = 0;
+ memset(s->regs, 0, ahc->nr_regs << 2);
s->total_req_len = 0;
}
@@ -445,11 +590,13 @@ static void aspeed_hace_realize(DeviceState *dev, Error **errp)
{
AspeedHACEState *s = ASPEED_HACE(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s);
sysbus_init_irq(sbd, &s->irq);
+ s->regs = g_new(uint32_t, ahc->nr_regs);
memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_hace_ops, s,
- TYPE_ASPEED_HACE, 0x1000);
+ TYPE_ASPEED_HACE, ahc->nr_regs << 2);
if (!s->dram_mr) {
error_setg(errp, TYPE_ASPEED_HACE ": 'dram' link not set");
@@ -469,21 +616,28 @@ static const Property aspeed_hace_properties[] = {
static const VMStateDescription vmstate_aspeed_hace = {
.name = TYPE_ASPEED_HACE,
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (const VMStateField[]) {
- VMSTATE_UINT32_ARRAY(regs, AspeedHACEState, ASPEED_HACE_NR_REGS),
VMSTATE_UINT32(total_req_len, AspeedHACEState),
- VMSTATE_UINT32(iov_count, AspeedHACEState),
VMSTATE_END_OF_LIST(),
}
};
+static void aspeed_hace_unrealize(DeviceState *dev)
+{
+ AspeedHACEState *s = ASPEED_HACE(dev);
+
+ g_free(s->regs);
+ s->regs = NULL;
+}
+
static void aspeed_hace_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = aspeed_hace_realize;
+ dc->unrealize = aspeed_hace_unrealize;
device_class_set_legacy_reset(dc, aspeed_hace_reset);
device_class_set_props(dc, aspeed_hace_properties);
dc->vmsd = &vmstate_aspeed_hace;
@@ -504,6 +658,7 @@ static void aspeed_ast2400_hace_class_init(ObjectClass *klass, const void *data)
dc->desc = "AST2400 Hash and Crypto Engine";
+ ahc->nr_regs = 0x64 >> 2;
ahc->src_mask = 0x0FFFFFFF;
ahc->dest_mask = 0x0FFFFFF8;
ahc->key_mask = 0x0FFFFFC0;
@@ -523,6 +678,7 @@ static void aspeed_ast2500_hace_class_init(ObjectClass *klass, const void *data)
dc->desc = "AST2500 Hash and Crypto Engine";
+ ahc->nr_regs = 0x64 >> 2;
ahc->src_mask = 0x3fffffff;
ahc->dest_mask = 0x3ffffff8;
ahc->key_mask = 0x3FFFFFC0;
@@ -542,6 +698,7 @@ static void aspeed_ast2600_hace_class_init(ObjectClass *klass, const void *data)
dc->desc = "AST2600 Hash and Crypto Engine";
+ ahc->nr_regs = 0x64 >> 2;
ahc->src_mask = 0x7FFFFFFF;
ahc->dest_mask = 0x7FFFFFF8;
ahc->key_mask = 0x7FFFFFF8;
@@ -561,6 +718,7 @@ static void aspeed_ast1030_hace_class_init(ObjectClass *klass, const void *data)
dc->desc = "AST1030 Hash and Crypto Engine";
+ ahc->nr_regs = 0x64 >> 2;
ahc->src_mask = 0x7FFFFFFF;
ahc->dest_mask = 0x7FFFFFF8;
ahc->key_mask = 0x7FFFFFF8;
@@ -580,17 +738,36 @@ static void aspeed_ast2700_hace_class_init(ObjectClass *klass, const void *data)
dc->desc = "AST2700 Hash and Crypto Engine";
+ ahc->nr_regs = 0x9C >> 2;
ahc->src_mask = 0x7FFFFFFF;
ahc->dest_mask = 0x7FFFFFF8;
ahc->key_mask = 0x7FFFFFF8;
ahc->hash_mask = 0x00147FFF;
/*
+ * The AST2700 supports a maximum DRAM size of 8 GB, with a DRAM
+ * addressable range from 0x0_0000_0000 to 0x1_FFFF_FFFF. Since this range
+ * fits within 34 bits, only bits [33:0] are needed to store the DRAM
+ * offset. To optimize address storage, the high physical address bits
+ * [1:0] of the source, digest and key buffer addresses are stored as
+ * dram_offset bits [33:32].
+ *
+ * This approach eliminates the need to reduce the high part of the DRAM
+ * physical address for DMA operations. Previously, this was calculated as
+ * (high physical address bits [7:0] - 4), since the DRAM start address is
+ * 0x4_00000000, making the high part address [7:0] - 4.
+ */
+ ahc->src_hi_mask = 0x00000003;
+ ahc->dest_hi_mask = 0x00000003;
+ ahc->key_hi_mask = 0x00000003;
+
+ /*
* Currently, it does not support the CRYPT command. Instead, it only
* sends an interrupt to notify the firmware that the crypt command
* has completed. It is a temporary workaround.
*/
ahc->raise_crypt_interrupt_workaround = true;
+ ahc->has_dma64 = true;
}
static const TypeInfo aspeed_ast2700_hace_info = {
diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c
index a7d101b..2fc5db7 100644
--- a/hw/misc/aspeed_sbc.c
+++ b/hw/misc/aspeed_sbc.c
@@ -15,9 +15,14 @@
#include "hw/misc/aspeed_sbc.h"
#include "qapi/error.h"
#include "migration/vmstate.h"
+#include "trace.h"
#define R_PROT (0x000 / 4)
+#define R_CMD (0x004 / 4)
+#define R_ADDR (0x010 / 4)
#define R_STATUS (0x014 / 4)
+#define R_CAMP1 (0x020 / 4)
+#define R_CAMP2 (0x024 / 4)
#define R_QSR (0x040 / 4)
/* R_STATUS */
@@ -41,6 +46,20 @@
#define QSR_RSA_MASK (0x3 << 12)
#define QSR_HASH_MASK (0x3 << 10)
+#define OTP_MEMORY_SIZE 0x4000
+/* OTP command */
+#define SBC_OTP_CMD_READ 0x23b1e361
+#define SBC_OTP_CMD_WRITE 0x23b1e362
+#define SBC_OTP_CMD_PROG 0x23b1e364
+
+#define OTP_DATA_DWORD_COUNT (0x800)
+#define OTP_TOTAL_DWORD_COUNT (0x1000)
+
+/* Voltage mode */
+#define MODE_REGISTER (0x1000)
+#define MODE_REGISTER_A (0x3000)
+#define MODE_REGISTER_B (0x5000)
+
static uint64_t aspeed_sbc_read(void *opaque, hwaddr addr, unsigned int size)
{
AspeedSBCState *s = ASPEED_SBC(opaque);
@@ -57,6 +76,142 @@ static uint64_t aspeed_sbc_read(void *opaque, hwaddr addr, unsigned int size)
return s->regs[addr];
}
+static bool aspeed_sbc_otp_read(AspeedSBCState *s,
+ uint32_t otp_addr)
+{
+ MemTxResult ret;
+ AspeedOTPState *otp = &s->otp;
+ uint32_t value, otp_offset;
+ bool is_data = false;
+
+ if (otp_addr < OTP_DATA_DWORD_COUNT) {
+ is_data = true;
+ } else if (otp_addr >= OTP_TOTAL_DWORD_COUNT) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Invalid OTP addr 0x%x\n",
+ otp_addr);
+ return false;
+ }
+
+ otp_offset = otp_addr << 2;
+ ret = address_space_read(&otp->as, otp_offset, MEMTXATTRS_UNSPECIFIED,
+ &value, sizeof(value));
+ if (ret != MEMTX_OK) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Failed to read OTP memory, addr = %x\n",
+ otp_addr);
+ return false;
+ }
+ s->regs[R_CAMP1] = value;
+ trace_aspeed_sbc_otp_read(otp_addr, value);
+
+ if (is_data) {
+ ret = address_space_read(&otp->as, otp_offset + 4,
+ MEMTXATTRS_UNSPECIFIED,
+ &value, sizeof(value));
+ if (ret != MEMTX_OK) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Failed to read OTP memory, addr = %x\n",
+ otp_addr);
+ return false;
+ }
+ s->regs[R_CAMP2] = value;
+ trace_aspeed_sbc_otp_read(otp_addr + 1, value);
+ }
+
+ return true;
+}
+
+static bool mode_handler(uint32_t otp_addr)
+{
+ switch (otp_addr) {
+ case MODE_REGISTER:
+ case MODE_REGISTER_A:
+ case MODE_REGISTER_B:
+ /* HW behavior, do nothing here */
+ return true;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Unsupported address 0x%x\n",
+ otp_addr);
+ return false;
+ }
+}
+
+static bool aspeed_sbc_otp_write(AspeedSBCState *s,
+ uint32_t otp_addr)
+{
+ if (otp_addr == 0) {
+ trace_aspeed_sbc_ignore_cmd(otp_addr);
+ return true;
+ } else {
+ if (mode_handler(otp_addr) == false) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool aspeed_sbc_otp_prog(AspeedSBCState *s,
+ uint32_t otp_addr)
+{
+ MemTxResult ret;
+ AspeedOTPState *otp = &s->otp;
+ uint32_t value = s->regs[R_CAMP1];
+
+ ret = address_space_write(&otp->as, otp_addr, MEMTXATTRS_UNSPECIFIED,
+ &value, sizeof(value));
+ if (ret != MEMTX_OK) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Failed to write OTP memory, addr = %x\n",
+ otp_addr);
+ return false;
+ }
+
+ trace_aspeed_sbc_otp_prog(otp_addr, value);
+
+ return true;
+}
+
+static void aspeed_sbc_handle_command(void *opaque, uint32_t cmd)
+{
+ AspeedSBCState *s = ASPEED_SBC(opaque);
+ AspeedSBCClass *sc = ASPEED_SBC_GET_CLASS(opaque);
+ bool ret = false;
+ uint32_t otp_addr;
+
+ if (!sc->has_otp) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: OTP memory is not supported\n",
+ __func__);
+ return;
+ }
+
+ s->regs[R_STATUS] &= ~(OTP_MEM_IDLE | OTP_IDLE);
+ otp_addr = s->regs[R_ADDR];
+
+ switch (cmd) {
+ case SBC_OTP_CMD_READ:
+ ret = aspeed_sbc_otp_read(s, otp_addr);
+ break;
+ case SBC_OTP_CMD_WRITE:
+ ret = aspeed_sbc_otp_write(s, otp_addr);
+ break;
+ case SBC_OTP_CMD_PROG:
+ ret = aspeed_sbc_otp_prog(s, otp_addr);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Unknown command 0x%x\n",
+ __func__, cmd);
+ break;
+ }
+
+ trace_aspeed_sbc_handle_cmd(cmd, otp_addr, ret);
+ s->regs[R_STATUS] |= (OTP_MEM_IDLE | OTP_IDLE);
+}
+
static void aspeed_sbc_write(void *opaque, hwaddr addr, uint64_t data,
unsigned int size)
{
@@ -78,6 +233,9 @@ static void aspeed_sbc_write(void *opaque, hwaddr addr, uint64_t data,
"%s: write to read only register 0x%" HWADDR_PRIx "\n",
__func__, addr << 2);
return;
+ case R_CMD:
+ aspeed_sbc_handle_command(opaque, data);
+ return;
default:
break;
}
@@ -115,10 +273,30 @@ static void aspeed_sbc_reset(DeviceState *dev)
s->regs[R_QSR] = s->signing_settings;
}
+static void aspeed_sbc_instance_init(Object *obj)
+{
+ AspeedSBCClass *sc = ASPEED_SBC_GET_CLASS(obj);
+ AspeedSBCState *s = ASPEED_SBC(obj);
+
+ if (sc->has_otp) {
+ object_initialize_child(OBJECT(s), "otp", &s->otp,
+ TYPE_ASPEED_OTP);
+ }
+}
+
static void aspeed_sbc_realize(DeviceState *dev, Error **errp)
{
AspeedSBCState *s = ASPEED_SBC(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ AspeedSBCClass *sc = ASPEED_SBC_GET_CLASS(dev);
+
+ if (sc->has_otp) {
+ object_property_set_int(OBJECT(&s->otp), "size",
+ OTP_MEMORY_SIZE, &error_abort);
+ if (!qdev_realize(DEVICE(&s->otp), NULL, errp)) {
+ return;
+ }
+ }
memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sbc_ops, s,
TYPE_ASPEED_SBC, 0x1000);
@@ -155,6 +333,7 @@ static const TypeInfo aspeed_sbc_info = {
.name = TYPE_ASPEED_SBC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AspeedSBCState),
+ .instance_init = aspeed_sbc_instance_init,
.class_init = aspeed_sbc_class_init,
.class_size = sizeof(AspeedSBCClass)
};
@@ -162,8 +341,10 @@ static const TypeInfo aspeed_sbc_info = {
static void aspeed_ast2600_sbc_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedSBCClass *sc = ASPEED_SBC_CLASS(klass);
dc->desc = "AST2600 Secure Boot Controller";
+ sc->has_otp = true;
}
static const TypeInfo aspeed_ast2600_sbc_info = {
@@ -172,9 +353,25 @@ static const TypeInfo aspeed_ast2600_sbc_info = {
.class_init = aspeed_ast2600_sbc_class_init,
};
+static void aspeed_ast10x0_sbc_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedSBCClass *sc = ASPEED_SBC_CLASS(klass);
+
+ dc->desc = "AST10X0 Secure Boot Controller";
+ sc->has_otp = true;
+}
+
+static const TypeInfo aspeed_ast10x0_sbc_info = {
+ .name = TYPE_ASPEED_AST10X0_SBC,
+ .parent = TYPE_ASPEED_SBC,
+ .class_init = aspeed_ast10x0_sbc_class_init,
+};
+
static void aspeed_sbc_register_types(void)
{
type_register_static(&aspeed_ast2600_sbc_info);
+ type_register_static(&aspeed_ast10x0_sbc_info);
type_register_static(&aspeed_sbc_info);
}
diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c
index 4930e00..a0ab5ee 100644
--- a/hw/misc/aspeed_scu.c
+++ b/hw/misc/aspeed_scu.c
@@ -91,6 +91,7 @@
#define BMC_DEV_ID TO_REG(0x1A4)
#define AST2600_PROT_KEY TO_REG(0x00)
+#define AST2600_PROT_KEY2 TO_REG(0x10)
#define AST2600_SILICON_REV TO_REG(0x04)
#define AST2600_SILICON_REV2 TO_REG(0x14)
#define AST2600_SYS_RST_CTRL TO_REG(0x40)
@@ -176,6 +177,7 @@
#define AST2700_SCUIO_UARTCLK_GEN TO_REG(0x330)
#define AST2700_SCUIO_HUARTCLK_GEN TO_REG(0x334)
#define AST2700_SCUIO_CLK_DUTY_MEAS_RST TO_REG(0x388)
+#define AST2700_SCUIO_FREQ_CNT_CTL TO_REG(0x3A0)
#define SCU_IO_REGION_SIZE 0x1000
@@ -722,6 +724,8 @@ static void aspeed_ast2600_scu_write(void *opaque, hwaddr offset,
int reg = TO_REG(offset);
/* Truncate here so bitwise operations below behave as expected */
uint32_t data = data64;
+ bool prot_data_state = data == ASPEED_SCU_PROT_KEY;
+ bool unlocked = s->regs[AST2600_PROT_KEY] && s->regs[AST2600_PROT_KEY2];
if (reg >= ASPEED_AST2600_SCU_NR_REGS) {
qemu_log_mask(LOG_GUEST_ERROR,
@@ -730,15 +734,24 @@ static void aspeed_ast2600_scu_write(void *opaque, hwaddr offset,
return;
}
- if (reg > PROT_KEY && !s->regs[PROT_KEY]) {
+ if ((reg != AST2600_PROT_KEY && reg != AST2600_PROT_KEY2) && !unlocked) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: SCU is locked!\n", __func__);
+ return;
}
trace_aspeed_scu_write(offset, size, data);
switch (reg) {
case AST2600_PROT_KEY:
- s->regs[reg] = (data == ASPEED_SCU_PROT_KEY) ? 1 : 0;
+ /*
+ * Writing a value to SCU000 will modify both protection
+ * registers to each protection register individually.
+ */
+ s->regs[AST2600_PROT_KEY] = prot_data_state;
+ s->regs[AST2600_PROT_KEY2] = prot_data_state;
+ return;
+ case AST2600_PROT_KEY2:
+ s->regs[AST2600_PROT_KEY2] = prot_data_state;
return;
case AST2600_HW_STRAP1:
case AST2600_HW_STRAP2:
@@ -1022,6 +1035,10 @@ static void aspeed_ast2700_scuio_write(void *opaque, hwaddr offset,
s->regs[reg - 1] ^= data;
updated = true;
break;
+ case AST2700_SCUIO_FREQ_CNT_CTL:
+ s->regs[reg] = deposit32(s->regs[reg], 6, 1, !!(data & BIT(1)));
+ updated = true;
+ break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Unhandled write at offset 0x%" HWADDR_PRIx "\n",
@@ -1066,6 +1083,7 @@ static const uint32_t ast2700_a0_resets_io[ASPEED_AST2700_SCU_NR_REGS] = {
[AST2700_SCUIO_UARTCLK_GEN] = 0x00014506,
[AST2700_SCUIO_HUARTCLK_GEN] = 0x000145c0,
[AST2700_SCUIO_CLK_DUTY_MEAS_RST] = 0x0c9100d2,
+ [AST2700_SCUIO_FREQ_CNT_CTL] = 0x00000080,
};
static void aspeed_2700_scuio_class_init(ObjectClass *klass, const void *data)
diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c
index f04d993..dff7cc3 100644
--- a/hw/misc/aspeed_sdmc.c
+++ b/hw/misc/aspeed_sdmc.c
@@ -570,6 +570,9 @@ static void aspeed_2700_sdmc_reset(DeviceState *dev)
/* Set ram size bit and defaults values */
s->regs[R_MAIN_CONF] = asc->compute_conf(s, 0);
+ /* Skipping dram init */
+ s->regs[R_MAIN_CONTROL] = BIT(16);
+
if (s->unlocked) {
s->regs[R_2700_PROT] = PROT_UNLOCKED;
}
diff --git a/hw/misc/ivshmem-flat.c b/hw/misc/ivshmem-flat.c
index be28c24..27ee8c9 100644
--- a/hw/misc/ivshmem-flat.c
+++ b/hw/misc/ivshmem-flat.c
@@ -138,6 +138,8 @@ static void ivshmem_flat_remove_peer(IvshmemFTState *s, uint16_t peer_id)
static void ivshmem_flat_add_vector(IvshmemFTState *s, IvshmemPeer *peer,
int vector_fd)
{
+ Error *err = NULL;
+
if (peer->vector_counter >= IVSHMEM_MAX_VECTOR_NUM) {
trace_ivshmem_flat_add_vector_failure(peer->vector_counter,
vector_fd, peer->id);
@@ -154,7 +156,10 @@ static void ivshmem_flat_add_vector(IvshmemFTState *s, IvshmemPeer *peer,
* peer.
*/
peer->vector[peer->vector_counter].id = peer->vector_counter;
- g_unix_set_fd_nonblocking(vector_fd, true, NULL);
+ if (!qemu_set_blocking(vector_fd, false, &err)) {
+ /* FIXME handle the error */
+ warn_report_err(err);
+ }
event_notifier_init_fd(&peer->vector[peer->vector_counter].event_notifier,
vector_fd);
@@ -362,7 +367,7 @@ static bool ivshmem_flat_connect_server(DeviceState *dev, Error **errp)
*
* ivshmem_flat_recv_msg() calls return 'msg' and 'fd'.
*
- * See ./docs/specs/ivshmem-spec.txt for details on the protocol.
+ * See docs/specs/ivshmem-spec.rst for details on the protocol.
*/
/* Step 0 */
diff --git a/hw/misc/ivshmem-pci.c b/hw/misc/ivshmem-pci.c
index 5a10bca..2748db9 100644
--- a/hw/misc/ivshmem-pci.c
+++ b/hw/misc/ivshmem-pci.c
@@ -479,6 +479,11 @@ static void process_msg_shmem(IVShmemState *s, int fd, Error **errp)
struct stat buf;
size_t size;
+ if (fd < 0) {
+ error_setg(errp, "server didn't provide fd with shared memory message");
+ return;
+ }
+
if (s->ivshmem_bar2) {
error_setg(errp, "server sent unexpected shared memory message");
close(fd);
@@ -535,7 +540,12 @@ static void process_msg_connect(IVShmemState *s, uint16_t posn, int fd,
IVSHMEM_DPRINTF("eventfds[%d][%d] = %d\n", posn, vector, fd);
event_notifier_init_fd(&peer->eventfds[vector], fd);
- g_unix_set_fd_nonblocking(fd, true, NULL); /* msix/irqfd poll non block */
+
+ /* msix/irqfd poll non block */
+ if (!qemu_set_blocking(fd, false, errp)) {
+ close(fd);
+ return;
+ }
if (posn == s->vm_id) {
setup_interrupt(s, vector, errp);
@@ -553,7 +563,9 @@ static void process_msg(IVShmemState *s, int64_t msg, int fd, Error **errp)
if (msg < -1 || msg > IVSHMEM_MAX_PEERS) {
error_setg(errp, "server sent invalid message %" PRId64, msg);
- close(fd);
+ if (fd >= 0) {
+ close(fd);
+ }
return;
}
diff --git a/hw/misc/max78000_aes.c b/hw/misc/max78000_aes.c
new file mode 100644
index 0000000..d883ddd
--- /dev/null
+++ b/hw/misc/max78000_aes.c
@@ -0,0 +1,229 @@
+/*
+ * 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)
diff --git a/hw/misc/max78000_gcr.c b/hw/misc/max78000_gcr.c
new file mode 100644
index 0000000..fbbc92c
--- /dev/null
+++ b/hw/misc/max78000_gcr.c
@@ -0,0 +1,351 @@
+/*
+ * MAX78000 Global Control Registers
+ *
+ * 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 "system/runstate.h"
+#include "migration/vmstate.h"
+#include "hw/qdev-properties.h"
+#include "hw/char/max78000_uart.h"
+#include "hw/misc/max78000_trng.h"
+#include "hw/misc/max78000_aes.h"
+#include "hw/misc/max78000_gcr.h"
+
+
+static void max78000_gcr_reset_hold(Object *obj, ResetType type)
+{
+ DeviceState *dev = DEVICE(obj);
+ Max78000GcrState *s = MAX78000_GCR(dev);
+ s->sysctrl = 0x21002;
+ s->rst0 = 0;
+ /* All clocks are always ready */
+ s->clkctrl = 0x3e140008;
+ s->pm = 0x3f000;
+ s->pclkdiv = 0;
+ s->pclkdis0 = 0xffffffff;
+ s->memctrl = 0x5;
+ s->memz = 0;
+ s->sysst = 0;
+ s->rst1 = 0;
+ s->pckdis1 = 0xffffffff;
+ s->eventen = 0;
+ s->revision = 0xa1;
+ s->sysie = 0;
+ s->eccerr = 0;
+ s->ecced = 0;
+ s->eccie = 0;
+ s->eccaddr = 0;
+}
+
+static uint64_t max78000_gcr_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ Max78000GcrState *s = opaque;
+
+ switch (addr) {
+ case SYSCTRL:
+ return s->sysctrl;
+
+ case RST0:
+ return s->rst0;
+
+ case CLKCTRL:
+ return s->clkctrl;
+
+ case PM:
+ return s->pm;
+
+ case PCLKDIV:
+ return s->pclkdiv;
+
+ case PCLKDIS0:
+ return s->pclkdis0;
+
+ case MEMCTRL:
+ return s->memctrl;
+
+ case MEMZ:
+ return s->memz;
+
+ case SYSST:
+ return s->sysst;
+
+ case RST1:
+ return s->rst1;
+
+ case PCKDIS1:
+ return s->pckdis1;
+
+ case EVENTEN:
+ return s->eventen;
+
+ case REVISION:
+ return s->revision;
+
+ case SYSIE:
+ return s->sysie;
+
+ case ECCERR:
+ return s->eccerr;
+
+ case ECCED:
+ return s->ecced;
+
+ case ECCIE:
+ return s->eccie;
+
+ case ECCADDR:
+ return s->eccaddr;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"
+ HWADDR_PRIx "\n", __func__, addr);
+ return 0;
+
+ }
+}
+
+static void max78000_gcr_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ Max78000GcrState *s = opaque;
+ uint32_t val = val64;
+ uint8_t zero[0xc000] = {0};
+ switch (addr) {
+ case SYSCTRL:
+ /* Checksum calculations always pass immediately */
+ s->sysctrl = (val & 0x30000) | 0x1002;
+ break;
+
+ case RST0:
+ if (val & SYSTEM_RESET) {
+ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ }
+ if (val & PERIPHERAL_RESET) {
+ /*
+ * Peripheral reset resets all peripherals. The CPU
+ * retains its state. The GPIO, watchdog timers, AoD,
+ * RAM retention, and general control registers (GCR),
+ * including the clock configuration, are unaffected.
+ */
+ val = UART2_RESET | UART1_RESET | UART0_RESET |
+ ADC_RESET | CNN_RESET | TRNG_RESET |
+ RTC_RESET | I2C0_RESET | SPI1_RESET |
+ TMR3_RESET | TMR2_RESET | TMR1_RESET |
+ TMR0_RESET | WDT0_RESET | DMA_RESET;
+ }
+ if (val & SOFT_RESET) {
+ /* Soft reset also resets GPIO */
+ val = UART2_RESET | UART1_RESET | UART0_RESET |
+ ADC_RESET | CNN_RESET | TRNG_RESET |
+ RTC_RESET | I2C0_RESET | SPI1_RESET |
+ TMR3_RESET | TMR2_RESET | TMR1_RESET |
+ TMR0_RESET | GPIO1_RESET | GPIO0_RESET |
+ DMA_RESET;
+ }
+ if (val & UART2_RESET) {
+ device_cold_reset(s->uart2);
+ }
+ if (val & UART1_RESET) {
+ device_cold_reset(s->uart1);
+ }
+ if (val & UART0_RESET) {
+ device_cold_reset(s->uart0);
+ }
+ if (val & TRNG_RESET) {
+ device_cold_reset(s->trng);
+ }
+ if (val & AES_RESET) {
+ device_cold_reset(s->aes);
+ }
+ /* TODO: As other devices are implemented, add them here */
+ break;
+
+ case CLKCTRL:
+ s->clkctrl = val | SYSCLK_RDY;
+ break;
+
+ case PM:
+ s->pm = val;
+ break;
+
+ case PCLKDIV:
+ s->pclkdiv = val;
+ break;
+
+ case PCLKDIS0:
+ s->pclkdis0 = val;
+ break;
+
+ case MEMCTRL:
+ s->memctrl = val;
+ break;
+
+ case MEMZ:
+ if (val & ram0) {
+ address_space_write(&s->sram_as, SYSRAM0_START,
+ MEMTXATTRS_UNSPECIFIED, zero, 0x8000);
+ }
+ if (val & ram1) {
+ address_space_write(&s->sram_as, SYSRAM1_START,
+ MEMTXATTRS_UNSPECIFIED, zero, 0x8000);
+ }
+ if (val & ram2) {
+ address_space_write(&s->sram_as, SYSRAM2_START,
+ MEMTXATTRS_UNSPECIFIED, zero, 0xC000);
+ }
+ if (val & ram3) {
+ address_space_write(&s->sram_as, SYSRAM3_START,
+ MEMTXATTRS_UNSPECIFIED, zero, 0x4000);
+ }
+ break;
+
+ case SYSST:
+ s->sysst = val;
+ break;
+
+ case RST1:
+ /* TODO: As other devices are implemented, add them here */
+ s->rst1 = val;
+ break;
+
+ case PCKDIS1:
+ s->pckdis1 = val;
+ break;
+
+ case EVENTEN:
+ s->eventen = val;
+ break;
+
+ case REVISION:
+ s->revision = val;
+ break;
+
+ case SYSIE:
+ s->sysie = val;
+ break;
+
+ case ECCERR:
+ s->eccerr = val;
+ break;
+
+ case ECCED:
+ s->ecced = val;
+ break;
+
+ case ECCIE:
+ s->eccie = val;
+ break;
+
+ case ECCADDR:
+ s->eccaddr = val;
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, addr);
+ break;
+
+ }
+}
+
+static const Property max78000_gcr_properties[] = {
+ DEFINE_PROP_LINK("sram", Max78000GcrState, sram,
+ TYPE_MEMORY_REGION, MemoryRegion*),
+ DEFINE_PROP_LINK("uart0", Max78000GcrState, uart0,
+ TYPE_MAX78000_UART, DeviceState*),
+ DEFINE_PROP_LINK("uart1", Max78000GcrState, uart1,
+ TYPE_MAX78000_UART, DeviceState*),
+ DEFINE_PROP_LINK("uart2", Max78000GcrState, uart2,
+ TYPE_MAX78000_UART, DeviceState*),
+ DEFINE_PROP_LINK("trng", Max78000GcrState, trng,
+ TYPE_MAX78000_TRNG, DeviceState*),
+ DEFINE_PROP_LINK("aes", Max78000GcrState, aes,
+ TYPE_MAX78000_AES, DeviceState*),
+};
+
+static const MemoryRegionOps max78000_gcr_ops = {
+ .read = max78000_gcr_read,
+ .write = max78000_gcr_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static const VMStateDescription vmstate_max78000_gcr = {
+ .name = TYPE_MAX78000_GCR,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(sysctrl, Max78000GcrState),
+ VMSTATE_UINT32(rst0, Max78000GcrState),
+ VMSTATE_UINT32(clkctrl, Max78000GcrState),
+ VMSTATE_UINT32(pm, Max78000GcrState),
+ VMSTATE_UINT32(pclkdiv, Max78000GcrState),
+ VMSTATE_UINT32(pclkdis0, Max78000GcrState),
+ VMSTATE_UINT32(memctrl, Max78000GcrState),
+ VMSTATE_UINT32(memz, Max78000GcrState),
+ VMSTATE_UINT32(sysst, Max78000GcrState),
+ VMSTATE_UINT32(rst1, Max78000GcrState),
+ VMSTATE_UINT32(pckdis1, Max78000GcrState),
+ VMSTATE_UINT32(eventen, Max78000GcrState),
+ VMSTATE_UINT32(revision, Max78000GcrState),
+ VMSTATE_UINT32(sysie, Max78000GcrState),
+ VMSTATE_UINT32(eccerr, Max78000GcrState),
+ VMSTATE_UINT32(ecced, Max78000GcrState),
+ VMSTATE_UINT32(eccie, Max78000GcrState),
+ VMSTATE_UINT32(eccaddr, Max78000GcrState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void max78000_gcr_init(Object *obj)
+{
+ Max78000GcrState *s = MAX78000_GCR(obj);
+
+ memory_region_init_io(&s->mmio, obj, &max78000_gcr_ops, s,
+ TYPE_MAX78000_GCR, 0x400);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+}
+
+static void max78000_gcr_realize(DeviceState *dev, Error **errp)
+{
+ Max78000GcrState *s = MAX78000_GCR(dev);
+
+ address_space_init(&s->sram_as, s->sram, "sram");
+}
+
+static void max78000_gcr_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ device_class_set_props(dc, max78000_gcr_properties);
+
+ dc->realize = max78000_gcr_realize;
+ dc->vmsd = &vmstate_max78000_gcr;
+ rc->phases.hold = max78000_gcr_reset_hold;
+}
+
+static const TypeInfo max78000_gcr_info = {
+ .name = TYPE_MAX78000_GCR,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Max78000GcrState),
+ .instance_init = max78000_gcr_init,
+ .class_init = max78000_gcr_class_init,
+};
+
+static void max78000_gcr_register_types(void)
+{
+ type_register_static(&max78000_gcr_info);
+}
+
+type_init(max78000_gcr_register_types)
diff --git a/hw/misc/max78000_icc.c b/hw/misc/max78000_icc.c
new file mode 100644
index 0000000..6f7d2b2
--- /dev/null
+++ b/hw/misc/max78000_icc.c
@@ -0,0 +1,120 @@
+/*
+ * MAX78000 Instruction Cache
+ *
+ * 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_icc.h"
+
+
+static uint64_t max78000_icc_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ Max78000IccState *s = opaque;
+ switch (addr) {
+ case ICC_INFO:
+ return s->info;
+
+ case ICC_SZ:
+ return s->sz;
+
+ case ICC_CTRL:
+ return s->ctrl;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, addr);
+ return 0;
+
+ }
+}
+
+static void max78000_icc_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ Max78000IccState *s = opaque;
+
+ switch (addr) {
+ case ICC_CTRL:
+ s->ctrl = 0x10000 | (val64 & 1);
+ break;
+
+ case ICC_INVALIDATE:
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, addr);
+ break;
+ }
+}
+
+static const MemoryRegionOps max78000_icc_ops = {
+ .read = max78000_icc_read,
+ .write = max78000_icc_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static const VMStateDescription max78000_icc_vmstate = {
+ .name = TYPE_MAX78000_ICC,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(info, Max78000IccState),
+ VMSTATE_UINT32(sz, Max78000IccState),
+ VMSTATE_UINT32(ctrl, Max78000IccState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void max78000_icc_reset_hold(Object *obj, ResetType type)
+{
+ Max78000IccState *s = MAX78000_ICC(obj);
+ s->info = 0;
+ s->sz = 0x10000010;
+ s->ctrl = 0x10000;
+}
+
+static void max78000_icc_init(Object *obj)
+{
+ Max78000IccState *s = MAX78000_ICC(obj);
+
+ memory_region_init_io(&s->mmio, obj, &max78000_icc_ops, s,
+ TYPE_MAX78000_ICC, 0x800);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static void max78000_icc_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ rc->phases.hold = max78000_icc_reset_hold;
+ dc->vmsd = &max78000_icc_vmstate;
+}
+
+static const TypeInfo max78000_icc_info = {
+ .name = TYPE_MAX78000_ICC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Max78000IccState),
+ .instance_init = max78000_icc_init,
+ .class_init = max78000_icc_class_init,
+};
+
+static void max78000_icc_register_types(void)
+{
+ type_register_static(&max78000_icc_info);
+}
+
+type_init(max78000_icc_register_types)
diff --git a/hw/misc/max78000_trng.c b/hw/misc/max78000_trng.c
new file mode 100644
index 0000000..ecdaef5
--- /dev/null
+++ b/hw/misc/max78000_trng.c
@@ -0,0 +1,139 @@
+/*
+ * MAX78000 True Random Number Generator
+ *
+ * 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_trng.h"
+#include "qemu/guest-random.h"
+
+static uint64_t max78000_trng_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ uint32_t data;
+
+ Max78000TrngState *s = opaque;
+ switch (addr) {
+ case CTRL:
+ return s->ctrl;
+
+ case STATUS:
+ return 1;
+
+ case DATA:
+ /*
+ * When interrupts are enabled, reading random data should cause a
+ * new interrupt to be generated; since there's always a random number
+ * available, we could qemu_set_irq(s->irq, s->ctrl & RND_IE). Because
+ * of how trng_write is set up, this is always a noop, so don't
+ */
+ qemu_guest_getrandom_nofail(&data, sizeof(data));
+ return data;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"
+ HWADDR_PRIx "\n", __func__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void max78000_trng_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ Max78000TrngState *s = opaque;
+ uint32_t val = val64;
+ switch (addr) {
+ case CTRL:
+ /* TODO: implement AES keygen */
+ s->ctrl = val;
+
+ /*
+ * This device models random number generation as taking 0 time.
+ * A new random number is always available, so the condition for the
+ * RND interrupt is always fulfilled; we can just set irq to 1.
+ */
+ if (val & RND_IE) {
+ qemu_set_irq(s->irq, 1);
+ } else{
+ qemu_set_irq(s->irq, 0);
+ }
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"
+ HWADDR_PRIx "\n", __func__, addr);
+ break;
+ }
+}
+
+static void max78000_trng_reset_hold(Object *obj, ResetType type)
+{
+ Max78000TrngState *s = MAX78000_TRNG(obj);
+ s->ctrl = 0;
+ s->status = 0;
+ s->data = 0;
+}
+
+static const MemoryRegionOps max78000_trng_ops = {
+ .read = max78000_trng_read,
+ .write = max78000_trng_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static const VMStateDescription max78000_trng_vmstate = {
+ .name = TYPE_MAX78000_TRNG,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(ctrl, Max78000TrngState),
+ VMSTATE_UINT32(status, Max78000TrngState),
+ VMSTATE_UINT32(data, Max78000TrngState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void max78000_trng_init(Object *obj)
+{
+ Max78000TrngState *s = MAX78000_TRNG(obj);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+
+ memory_region_init_io(&s->mmio, obj, &max78000_trng_ops, s,
+ TYPE_MAX78000_TRNG, 0x1000);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+}
+
+static void max78000_trng_class_init(ObjectClass *klass, const void *data)
+{
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ rc->phases.hold = max78000_trng_reset_hold;
+ dc->vmsd = &max78000_trng_vmstate;
+
+}
+
+static const TypeInfo max78000_trng_info = {
+ .name = TYPE_MAX78000_TRNG,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Max78000TrngState),
+ .instance_init = max78000_trng_init,
+ .class_init = max78000_trng_class_init,
+};
+
+static void max78000_trng_register_types(void)
+{
+ type_register_static(&max78000_trng_info);
+}
+
+type_init(max78000_trng_register_types)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 6d47de4..b1d8d8e 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -70,6 +70,10 @@ 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'))
system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
'npcm_clk.c',
'npcm_gcr.c',
diff --git a/hw/misc/stm32_rcc.c b/hw/misc/stm32_rcc.c
index 94e8dae..5815b3e 100644
--- a/hw/misc/stm32_rcc.c
+++ b/hw/misc/stm32_rcc.c
@@ -60,7 +60,7 @@ static void stm32_rcc_write(void *opaque, hwaddr addr,
uint32_t value = val64;
uint32_t prev_value, new_value, irq_offset;
- trace_stm32_rcc_write(value, addr);
+ trace_stm32_rcc_write(addr, value);
if (addr > STM32_RCC_DCKCFGR2) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 4383808..eeb9243 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -90,6 +90,12 @@ slavio_sysctrl_mem_readl(uint32_t ret) "Read system control 0x%08x"
slavio_led_mem_writew(uint32_t val) "Write diagnostic LED 0x%04x"
slavio_led_mem_readw(uint32_t ret) "Read diagnostic LED 0x%04x"
+# aspeed_sbc.c
+aspeed_sbc_ignore_cmd(uint32_t cmd) "Ignoring command 0x%" PRIx32
+aspeed_sbc_handle_cmd(uint32_t cmd, uint32_t addr, bool ret) "Handling command 0x%" PRIx32 " for OTP addr 0x%" PRIx32 " Result: %d"
+aspeed_sbc_otp_read(uint32_t addr, uint32_t value) "OTP Memory read: addr 0x%" PRIx32 " value 0x%" PRIx32
+aspeed_sbc_otp_prog(uint32_t addr, uint32_t value) "OTP Memory write: addr 0x%" PRIx32 " value 0x%" PRIx32
+
# aspeed_scu.c
aspeed_scu_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32
aspeed_scu_read(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32
@@ -302,6 +308,14 @@ aspeed_peci_read(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%"
aspeed_peci_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64
aspeed_peci_raise_interrupt(uint32_t ctrl, uint32_t status) "ctrl 0x%" PRIx32 " status 0x%" PRIx32
+# aspeed_hace.c
+aspeed_hace_read(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64
+aspeed_hace_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64
+aspeed_hace_hash_sg(int index, uint64_t list_addr, uint64_t buf_addr, uint32_t len) "%d: list_addr 0x%" PRIx64 " buf_addr 0x%" PRIx64 " len 0x%" PRIx32
+aspeed_hace_hash_addr(const char *s, uint64_t addr) "%s: 0x%" PRIx64
+aspeed_hace_hash_execute_acc_mode(bool final_request) "final request: %d"
+aspeed_hace_hexdump(const char *desc, uint32_t offset, char *s) "%s: 0x%08x: %s"
+
# bcm2835_property.c
bcm2835_mbox_property(uint32_t tag, uint32_t bufsize, size_t resplen) "mbox property tag:0x%08x in_sz:%u out_sz:%zu"
diff --git a/hw/misc/xlnx-versal-cframe-reg.c b/hw/misc/xlnx-versal-cframe-reg.c
index 1ce083e..95e167b 100644
--- a/hw/misc/xlnx-versal-cframe-reg.c
+++ b/hw/misc/xlnx-versal-cframe-reg.c
@@ -693,6 +693,14 @@ static void cframe_reg_init(Object *obj)
fifo32_create(&s->new_f_data, FRAME_NUM_WORDS);
}
+static void cframe_reg_finalize(Object *obj)
+{
+ XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(obj);
+
+ fifo32_destroy(&s->new_f_data);
+ g_tree_destroy(s->cframes);
+}
+
static const VMStateDescription vmstate_cframe = {
.name = "cframe",
.version_id = 1,
@@ -833,6 +841,7 @@ static const TypeInfo cframe_reg_info = {
.instance_size = sizeof(XlnxVersalCFrameReg),
.class_init = cframe_reg_class_init,
.instance_init = cframe_reg_init,
+ .instance_finalize = cframe_reg_finalize,
.interfaces = (const InterfaceInfo[]) {
{ TYPE_XLNX_CFI_IF },
{ }
diff --git a/hw/misc/xlnx-versal-crl.c b/hw/misc/xlnx-versal-crl.c
index 08ff2fc..10e6af0 100644
--- a/hw/misc/xlnx-versal-crl.c
+++ b/hw/misc/xlnx-versal-crl.c
@@ -1,16 +1,13 @@
/*
* QEMU model of the Clock-Reset-LPD (CRL).
*
- * Copyright (c) 2022 Advanced Micro Devices, Inc.
+ * Copyright (c) 2022-2025 Advanced Micro Devices, Inc.
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Written by Edgar E. Iglesias <edgar.iglesias@amd.com>
*/
#include "qemu/osdep.h"
-#include "qapi/error.h"
-#include "qemu/log.h"
-#include "qemu/bitops.h"
#include "migration/vmstate.h"
#include "hw/qdev-properties.h"
#include "hw/sysbus.h"
@@ -58,90 +55,144 @@ static uint64_t crl_disable_prew(RegisterInfo *reg, uint64_t val64)
return 0;
}
-static void crl_reset_dev(XlnxVersalCRL *s, DeviceState *dev,
- bool rst_old, bool rst_new)
+static DeviceState **versal_decode_periph_rst(XlnxVersalCRLBase *s,
+ hwaddr addr, size_t *count)
{
- device_cold_reset(dev);
-}
+ size_t idx;
+ XlnxVersalCRL *xvc = XLNX_VERSAL_CRL(s);
-static void crl_reset_cpu(XlnxVersalCRL *s, ARMCPU *armcpu,
- bool rst_old, bool rst_new)
-{
- if (rst_new) {
- arm_set_cpu_off(arm_cpu_mp_affinity(armcpu));
- } else {
- arm_set_cpu_on_and_reset(arm_cpu_mp_affinity(armcpu));
- }
-}
+ *count = 1;
-#define REGFIELD_RESET(type, s, reg, f, new_val, dev) { \
- bool old_f = ARRAY_FIELD_EX32((s)->regs, reg, f); \
- bool new_f = FIELD_EX32(new_val, reg, f); \
- \
- /* Detect edges. */ \
- if (dev && old_f != new_f) { \
- crl_reset_ ## type(s, dev, old_f, new_f); \
- } \
-}
+ switch (addr) {
+ case A_RST_CPU_R5:
+ return xvc->cfg.rpu;
-static uint64_t crl_rst_r5_prew(RegisterInfo *reg, uint64_t val64)
-{
- XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque);
+ case A_RST_ADMA:
+ /* A single register fans out to all DMA reset inputs */
+ *count = ARRAY_SIZE(xvc->cfg.adma);
+ return xvc->cfg.adma;
- REGFIELD_RESET(cpu, s, RST_CPU_R5, RESET_CPU0, val64, s->cfg.cpu_r5[0]);
- REGFIELD_RESET(cpu, s, RST_CPU_R5, RESET_CPU1, val64, s->cfg.cpu_r5[1]);
- return val64;
-}
+ case A_RST_UART0 ... A_RST_UART1:
+ idx = (addr - A_RST_UART0) / sizeof(uint32_t);
+ return xvc->cfg.uart + idx;
-static uint64_t crl_rst_adma_prew(RegisterInfo *reg, uint64_t val64)
-{
- XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque);
- int i;
+ case A_RST_GEM0 ... A_RST_GEM1:
+ idx = (addr - A_RST_GEM0) / sizeof(uint32_t);
+ return xvc->cfg.gem + idx;
- /* A single register fans out to all ADMA reset inputs. */
- for (i = 0; i < ARRAY_SIZE(s->cfg.adma); i++) {
- REGFIELD_RESET(dev, s, RST_ADMA, RESET, val64, s->cfg.adma[i]);
+ case A_RST_USB0:
+ return xvc->cfg.usb;
+
+ default:
+ /* invalid or unimplemented */
+ g_assert_not_reached();
}
- return val64;
}
-static uint64_t crl_rst_uart0_prew(RegisterInfo *reg, uint64_t val64)
+static DeviceState **versal2_decode_periph_rst(XlnxVersalCRLBase *s,
+ hwaddr addr, size_t *count)
{
- XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque);
+ size_t idx;
+ XlnxVersal2CRL *xvc = XLNX_VERSAL2_CRL(s);
- REGFIELD_RESET(dev, s, RST_UART0, RESET, val64, s->cfg.uart[0]);
- return val64;
-}
+ *count = 1;
-static uint64_t crl_rst_uart1_prew(RegisterInfo *reg, uint64_t val64)
-{
- XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque);
+ switch (addr) {
+ case A_VERSAL2_RST_RPU_A ... A_VERSAL2_RST_RPU_E:
+ idx = (addr - A_VERSAL2_RST_RPU_A) / sizeof(uint32_t);
+ idx *= 2; /* two RPUs per RST_RPU_x registers */
+ return xvc->cfg.rpu + idx;
- REGFIELD_RESET(dev, s, RST_UART1, RESET, val64, s->cfg.uart[1]);
- return val64;
-}
+ case A_VERSAL2_RST_ADMA:
+ /* A single register fans out to all DMA reset inputs */
+ *count = ARRAY_SIZE(xvc->cfg.adma);
+ return xvc->cfg.adma;
-static uint64_t crl_rst_gem0_prew(RegisterInfo *reg, uint64_t val64)
-{
- XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque);
+ case A_VERSAL2_RST_SDMA:
+ *count = ARRAY_SIZE(xvc->cfg.sdma);
+ return xvc->cfg.sdma;
- REGFIELD_RESET(dev, s, RST_GEM0, RESET, val64, s->cfg.gem[0]);
- return val64;
+ case A_VERSAL2_RST_UART0 ... A_VERSAL2_RST_UART1:
+ idx = (addr - A_VERSAL2_RST_UART0) / sizeof(uint32_t);
+ return xvc->cfg.uart + idx;
+
+ case A_VERSAL2_RST_GEM0 ... A_VERSAL2_RST_GEM1:
+ idx = (addr - A_VERSAL2_RST_GEM0) / sizeof(uint32_t);
+ return xvc->cfg.gem + idx;
+
+ case A_VERSAL2_RST_USB0 ... A_VERSAL2_RST_USB1:
+ idx = (addr - A_VERSAL2_RST_USB0) / sizeof(uint32_t);
+ return xvc->cfg.usb + idx;
+
+ case A_VERSAL2_RST_CAN0 ... A_VERSAL2_RST_CAN3:
+ idx = (addr - A_VERSAL2_RST_CAN0) / sizeof(uint32_t);
+ return xvc->cfg.can + idx;
+
+ default:
+ /* invalid or unimplemented */
+ return NULL;
+ }
}
-static uint64_t crl_rst_gem1_prew(RegisterInfo *reg, uint64_t val64)
+static uint64_t crl_rst_cpu_prew(RegisterInfo *reg, uint64_t val64)
{
- XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque);
+ XlnxVersalCRLBase *s = XLNX_VERSAL_CRL_BASE(reg->opaque);
+ XlnxVersalCRLBaseClass *xvcbc = XLNX_VERSAL_CRL_BASE_GET_CLASS(s);
+ DeviceState **dev;
+ size_t i, count;
+
+ dev = xvcbc->decode_periph_rst(s, reg->access->addr, &count);
+
+ for (i = 0; i < 2; i++) {
+ bool prev, new;
+ uint64_t aff;
+
+ prev = extract32(s->regs[reg->access->addr / 4], i, 1);
+ new = extract32(val64, i, 1);
+
+ if (prev == new) {
+ continue;
+ }
+
+ aff = arm_cpu_mp_affinity(ARM_CPU(dev[i]));
+
+ if (new) {
+ arm_set_cpu_off(aff);
+ } else {
+ arm_set_cpu_on_and_reset(aff);
+ }
+ }
- REGFIELD_RESET(dev, s, RST_GEM1, RESET, val64, s->cfg.gem[1]);
return val64;
}
-static uint64_t crl_rst_usb_prew(RegisterInfo *reg, uint64_t val64)
+static uint64_t crl_rst_dev_prew(RegisterInfo *reg, uint64_t val64)
{
- XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque);
+ XlnxVersalCRLBase *s = XLNX_VERSAL_CRL_BASE(reg->opaque);
+ XlnxVersalCRLBaseClass *xvcbc = XLNX_VERSAL_CRL_BASE_GET_CLASS(s);
+ DeviceState **dev;
+ bool prev, new;
+ size_t i, count;
+
+ dev = xvcbc->decode_periph_rst(s, reg->access->addr, &count);
+
+ if (dev == NULL) {
+ return val64;
+ }
+
+ prev = s->regs[reg->access->addr / 4] & 0x1;
+ new = val64 & 0x1;
+
+ if (prev == new) {
+ return val64;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (dev[i]) {
+ device_cold_reset(dev[i]);
+ }
+ }
- REGFIELD_RESET(dev, s, RST_USB0, RESET, val64, s->cfg.usb);
return val64;
}
@@ -247,27 +298,27 @@ static const RegisterAccessInfo crl_regs_info[] = {
},{ .name = "RST_CPU_R5", .addr = A_RST_CPU_R5,
.reset = 0x17,
.rsvd = 0x8,
- .pre_write = crl_rst_r5_prew,
+ .pre_write = crl_rst_cpu_prew,
},{ .name = "RST_ADMA", .addr = A_RST_ADMA,
.reset = 0x1,
- .pre_write = crl_rst_adma_prew,
+ .pre_write = crl_rst_dev_prew,
},{ .name = "RST_GEM0", .addr = A_RST_GEM0,
.reset = 0x1,
- .pre_write = crl_rst_gem0_prew,
+ .pre_write = crl_rst_dev_prew,
},{ .name = "RST_GEM1", .addr = A_RST_GEM1,
.reset = 0x1,
- .pre_write = crl_rst_gem1_prew,
+ .pre_write = crl_rst_dev_prew,
},{ .name = "RST_SPARE", .addr = A_RST_SPARE,
.reset = 0x1,
},{ .name = "RST_USB0", .addr = A_RST_USB0,
.reset = 0x1,
- .pre_write = crl_rst_usb_prew,
+ .pre_write = crl_rst_dev_prew,
},{ .name = "RST_UART0", .addr = A_RST_UART0,
.reset = 0x1,
- .pre_write = crl_rst_uart0_prew,
+ .pre_write = crl_rst_dev_prew,
},{ .name = "RST_UART1", .addr = A_RST_UART1,
.reset = 0x1,
- .pre_write = crl_rst_uart1_prew,
+ .pre_write = crl_rst_dev_prew,
},{ .name = "RST_SPI0", .addr = A_RST_SPI0,
.reset = 0x1,
},{ .name = "RST_SPI1", .addr = A_RST_SPI1,
@@ -301,7 +352,247 @@ static const RegisterAccessInfo crl_regs_info[] = {
}
};
-static void crl_reset_enter(Object *obj, ResetType type)
+static const RegisterAccessInfo versal2_crl_regs_info[] = {
+ { .name = "ERR_CTRL", .addr = A_VERSAL2_ERR_CTRL,
+ .reset = 0x1,
+ },{ .name = "WPROT", .addr = A_VERSAL2_WPROT,
+ },{ .name = "RPLL_CTRL", .addr = A_VERSAL2_RPLL_CTRL,
+ .reset = 0x24809,
+ .rsvd = 0xf88c00f6,
+ },{ .name = "RPLL_CFG", .addr = A_VERSAL2_RPLL_CFG,
+ .reset = 0x7e5dcc6c,
+ .rsvd = 0x1801210,
+ },{ .name = "FLXPLL_CTRL", .addr = A_VERSAL2_FLXPLL_CTRL,
+ .reset = 0x24809,
+ .rsvd = 0xf88c00f6,
+ },{ .name = "FLXPLL_CFG", .addr = A_VERSAL2_FLXPLL_CFG,
+ .reset = 0x7e5dcc6c,
+ .rsvd = 0x1801210,
+ },{ .name = "PLL_STATUS", .addr = A_VERSAL2_PLL_STATUS,
+ .reset = 0xf,
+ .rsvd = 0xf0,
+ .ro = 0xf,
+ },{ .name = "RPLL_TO_XPD_CTRL", .addr = A_VERSAL2_RPLL_TO_XPD_CTRL,
+ .reset = 0x2000100,
+ .rsvd = 0xfdfc00ff,
+ },{ .name = "LPX_TOP_SWITCH_CTRL", .addr = A_VERSAL2_LPX_TOP_SWITCH_CTRL,
+ .reset = 0xe000300,
+ .rsvd = 0xf1fc00f8,
+ },{ .name = "LPX_LSBUS_CLK_CTRL", .addr = A_VERSAL2_LPX_LSBUS_CLK_CTRL,
+ .reset = 0x2000800,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "RPU_CLK_CTRL", .addr = A_VERSAL2_RPU_CLK_CTRL,
+ .reset = 0x3f00300,
+ .rsvd = 0xfc0c00f8,
+ },{ .name = "OCM_CLK_CTRL", .addr = A_VERSAL2_OCM_CLK_CTRL,
+ .reset = 0x1e00000,
+ .rsvd = 0xfe1fffff,
+ },{ .name = "IOU_SWITCH_CLK_CTRL", .addr = A_VERSAL2_IOU_SWITCH_CLK_CTRL,
+ .reset = 0x2000500,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "GEM0_REF_CTRL", .addr = A_VERSAL2_GEM0_REF_CTRL,
+ .reset = 0xe000a00,
+ .rsvd = 0xf1fc00f8,
+ },{ .name = "GEM1_REF_CTRL", .addr = A_VERSAL2_GEM1_REF_CTRL,
+ .reset = 0xe000a00,
+ .rsvd = 0xf1fc00f8,
+ },{ .name = "GEM_TSU_REF_CLK_CTRL", .addr = A_VERSAL2_GEM_TSU_REF_CLK_CTRL,
+ .reset = 0x300,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "USB0_BUS_REF_CLK_CTRL",
+ .addr = A_VERSAL2_USB0_BUS_REF_CLK_CTRL,
+ .reset = 0x2001900,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "USB1_BUS_REF_CLK_CTRL",
+ .addr = A_VERSAL2_USB1_BUS_REF_CLK_CTRL,
+ .reset = 0x2001900,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "UART0_REF_CLK_CTRL", .addr = A_VERSAL2_UART0_REF_CLK_CTRL,
+ .reset = 0xc00,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "UART1_REF_CLK_CTRL", .addr = A_VERSAL2_UART1_REF_CLK_CTRL,
+ .reset = 0xc00,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "SPI0_REF_CLK_CTRL", .addr = A_VERSAL2_SPI0_REF_CLK_CTRL,
+ .reset = 0x600,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "SPI1_REF_CLK_CTRL", .addr = A_VERSAL2_SPI1_REF_CLK_CTRL,
+ .reset = 0x600,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "CAN0_REF_2X_CTRL", .addr = A_VERSAL2_CAN0_REF_2X_CTRL,
+ .reset = 0xc00,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "CAN1_REF_2X_CTRL", .addr = A_VERSAL2_CAN1_REF_2X_CTRL,
+ .reset = 0xc00,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "CAN2_REF_2X_CTRL", .addr = A_VERSAL2_CAN2_REF_2X_CTRL,
+ .reset = 0xc00,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "CAN3_REF_2X_CTRL", .addr = A_VERSAL2_CAN3_REF_2X_CTRL,
+ .reset = 0xc00,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "I3C0_REF_CTRL", .addr = A_VERSAL2_I3C0_REF_CTRL,
+ .reset = 0x2000c00,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "I3C1_REF_CTRL", .addr = A_VERSAL2_I3C1_REF_CTRL,
+ .reset = 0x2000c00,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "I3C2_REF_CTRL", .addr = A_VERSAL2_I3C2_REF_CTRL,
+ .reset = 0x2000c00,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "I3C3_REF_CTRL", .addr = A_VERSAL2_I3C3_REF_CTRL,
+ .reset = 0x2000c00,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "I3C4_REF_CTRL", .addr = A_VERSAL2_I3C4_REF_CTRL,
+ .reset = 0x2000c00,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "I3C5_REF_CTRL", .addr = A_VERSAL2_I3C5_REF_CTRL,
+ .reset = 0x2000c00,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "I3C6_REF_CTRL", .addr = A_VERSAL2_I3C6_REF_CTRL,
+ .reset = 0x2000c00,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "I3C7_REF_CTRL", .addr = A_VERSAL2_I3C7_REF_CTRL,
+ .reset = 0x2000c00,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "DBG_LPX_CTRL", .addr = A_VERSAL2_DBG_LPX_CTRL,
+ .reset = 0x300,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "TIMESTAMP_REF_CTRL", .addr = A_VERSAL2_TIMESTAMP_REF_CTRL,
+ .reset = 0x2000c00,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "SAFETY_CHK", .addr = A_VERSAL2_SAFETY_CHK,
+ },{ .name = "ASU_CLK_CTRL", .addr = A_VERSAL2_ASU_CLK_CTRL,
+ .reset = 0x2000f04,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "DBG_TSTMP_CLK_CTRL", .addr = A_VERSAL2_DBG_TSTMP_CLK_CTRL,
+ .reset = 0x300,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "MMI_TOPSW_CLK_CTRL", .addr = A_VERSAL2_MMI_TOPSW_CLK_CTRL,
+ .reset = 0x2000300,
+ .rsvd = 0xfdfc00f8,
+ },{ .name = "WWDT_PLL_CLK_CTRL", .addr = A_VERSAL2_WWDT_PLL_CLK_CTRL,
+ .reset = 0xc00,
+ .rsvd = 0xfffc00f8,
+ },{ .name = "RCLK_CTRL", .addr = A_VERSAL2_RCLK_CTRL,
+ .rsvd = 0xc040,
+ },{ .name = "RST_RPU_A", .addr = A_VERSAL2_RST_RPU_A,
+ .reset = 0x10303,
+ .rsvd = 0xfffefcfc,
+ .pre_write = crl_rst_cpu_prew,
+ },{ .name = "RST_RPU_B", .addr = A_VERSAL2_RST_RPU_B,
+ .reset = 0x10303,
+ .rsvd = 0xfffefcfc,
+ .pre_write = crl_rst_cpu_prew,
+ },{ .name = "RST_RPU_C", .addr = A_VERSAL2_RST_RPU_C,
+ .reset = 0x10303,
+ .rsvd = 0xfffefcfc,
+ .pre_write = crl_rst_cpu_prew,
+ },{ .name = "RST_RPU_D", .addr = A_VERSAL2_RST_RPU_D,
+ .reset = 0x10303,
+ .rsvd = 0xfffefcfc,
+ .pre_write = crl_rst_cpu_prew,
+ },{ .name = "RST_RPU_E", .addr = A_VERSAL2_RST_RPU_E,
+ .reset = 0x10303,
+ .rsvd = 0xfffefcfc,
+ .pre_write = crl_rst_cpu_prew,
+ },{ .name = "RST_RPU_GD_0", .addr = A_VERSAL2_RST_RPU_GD_0,
+ .reset = 0x3,
+ },{ .name = "RST_RPU_GD_1", .addr = A_VERSAL2_RST_RPU_GD_1,
+ .reset = 0x3,
+ },{ .name = "RST_ASU_GD", .addr = A_VERSAL2_RST_ASU_GD,
+ .reset = 0x3,
+ },{ .name = "RST_ADMA", .addr = A_VERSAL2_RST_ADMA,
+ .reset = 0x1,
+ .pre_write = crl_rst_dev_prew,
+ },{ .name = "RST_SDMA", .addr = A_VERSAL2_RST_SDMA,
+ .pre_write = crl_rst_dev_prew,
+ .reset = 0x1,
+ },{ .name = "RST_GEM0", .addr = A_VERSAL2_RST_GEM0,
+ .reset = 0x1,
+ .pre_write = crl_rst_dev_prew,
+ },{ .name = "RST_GEM1", .addr = A_VERSAL2_RST_GEM1,
+ .reset = 0x1,
+ .pre_write = crl_rst_dev_prew,
+ },{ .name = "RST_USB0", .addr = A_VERSAL2_RST_USB0,
+ .reset = 0x1,
+ .pre_write = crl_rst_dev_prew,
+ },{ .name = "RST_USB1", .addr = A_VERSAL2_RST_USB1,
+ .reset = 0x1,
+ .pre_write = crl_rst_dev_prew,
+ },{ .name = "RST_UART0", .addr = A_VERSAL2_RST_UART0,
+ .reset = 0x1,
+ .pre_write = crl_rst_dev_prew,
+ },{ .name = "RST_UART1", .addr = A_VERSAL2_RST_UART1,
+ .reset = 0x1,
+ .pre_write = crl_rst_dev_prew,
+ },{ .name = "RST_SPI0", .addr = A_VERSAL2_RST_SPI0,
+ .reset = 0x1,
+ },{ .name = "RST_SPI1", .addr = A_VERSAL2_RST_SPI1,
+ .reset = 0x1,
+ },{ .name = "RST_CAN0", .addr = A_VERSAL2_RST_CAN0,
+ .reset = 0x1,
+ .pre_write = crl_rst_dev_prew,
+ },{ .name = "RST_CAN1", .addr = A_VERSAL2_RST_CAN1,
+ .reset = 0x1,
+ .pre_write = crl_rst_dev_prew,
+ },{ .name = "RST_CAN2", .addr = A_VERSAL2_RST_CAN2,
+ .reset = 0x1,
+ .pre_write = crl_rst_dev_prew,
+ },{ .name = "RST_CAN3", .addr = A_VERSAL2_RST_CAN3,
+ .reset = 0x1,
+ .pre_write = crl_rst_dev_prew,
+ },{ .name = "RST_I3C0", .addr = A_VERSAL2_RST_I3C0,
+ .reset = 0x1,
+ },{ .name = "RST_I3C1", .addr = A_VERSAL2_RST_I3C1,
+ .reset = 0x1,
+ },{ .name = "RST_I3C2", .addr = A_VERSAL2_RST_I3C2,
+ .reset = 0x1,
+ },{ .name = "RST_I3C3", .addr = A_VERSAL2_RST_I3C3,
+ .reset = 0x1,
+ },{ .name = "RST_I3C4", .addr = A_VERSAL2_RST_I3C4,
+ .reset = 0x1,
+ },{ .name = "RST_I3C5", .addr = A_VERSAL2_RST_I3C5,
+ .reset = 0x1,
+ },{ .name = "RST_I3C6", .addr = A_VERSAL2_RST_I3C6,
+ .reset = 0x1,
+ },{ .name = "RST_I3C7", .addr = A_VERSAL2_RST_I3C7,
+ .reset = 0x1,
+ },{ .name = "RST_DBG_LPX", .addr = A_VERSAL2_RST_DBG_LPX,
+ .reset = 0x3,
+ .rsvd = 0xfc,
+ },{ .name = "RST_GPIO", .addr = A_VERSAL2_RST_GPIO,
+ .reset = 0x1,
+ },{ .name = "RST_TTC", .addr = A_VERSAL2_RST_TTC,
+ .reset = 0xff,
+ },{ .name = "RST_TIMESTAMP", .addr = A_VERSAL2_RST_TIMESTAMP,
+ .reset = 0x1,
+ },{ .name = "RST_SWDT0", .addr = A_VERSAL2_RST_SWDT0,
+ .reset = 0x1,
+ },{ .name = "RST_SWDT1", .addr = A_VERSAL2_RST_SWDT1,
+ .reset = 0x1,
+ },{ .name = "RST_SWDT2", .addr = A_VERSAL2_RST_SWDT2,
+ .reset = 0x1,
+ },{ .name = "RST_SWDT3", .addr = A_VERSAL2_RST_SWDT3,
+ .reset = 0x1,
+ },{ .name = "RST_SWDT4", .addr = A_VERSAL2_RST_SWDT4,
+ .reset = 0x1,
+ },{ .name = "RST_IPI", .addr = A_VERSAL2_RST_IPI,
+ },{ .name = "RST_SYSMON", .addr = A_VERSAL2_RST_SYSMON,
+ },{ .name = "ASU_MB_RST_MODE", .addr = A_VERSAL2_ASU_MB_RST_MODE,
+ .reset = 0x1,
+ .rsvd = 0xf8,
+ },{ .name = "FPX_TOPSW_MUX_CTRL", .addr = A_VERSAL2_FPX_TOPSW_MUX_CTRL,
+ .reset = 0x1,
+ },{ .name = "RST_FPX", .addr = A_VERSAL2_RST_FPX,
+ .reset = 0x3,
+ },{ .name = "RST_MMI", .addr = A_VERSAL2_RST_MMI,
+ .reset = 0x1,
+ },{ .name = "RST_OCM", .addr = A_VERSAL2_RST_OCM,
+ }
+};
+
+static void versal_crl_reset_enter(Object *obj, ResetType type)
{
XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj);
unsigned int i;
@@ -311,7 +602,17 @@ static void crl_reset_enter(Object *obj, ResetType type)
}
}
-static void crl_reset_hold(Object *obj, ResetType type)
+static void versal2_crl_reset_enter(Object *obj, ResetType type)
+{
+ XlnxVersal2CRL *s = XLNX_VERSAL2_CRL(obj);
+ size_t i;
+
+ for (i = 0; i < VERSAL2_CRL_R_MAX; ++i) {
+ register_reset(&s->regs_info[i]);
+ }
+}
+
+static void versal_crl_reset_hold(Object *obj, ResetType type)
{
XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj);
@@ -328,25 +629,27 @@ static const MemoryRegionOps crl_ops = {
},
};
-static void crl_init(Object *obj)
+static void versal_crl_init(Object *obj)
{
XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj);
+ XlnxVersalCRLBase *xvcb = XLNX_VERSAL_CRL_BASE(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
int i;
- s->reg_array =
+ xvcb->reg_array =
register_init_block32(DEVICE(obj), crl_regs_info,
ARRAY_SIZE(crl_regs_info),
s->regs_info, s->regs,
&crl_ops,
XLNX_VERSAL_CRL_ERR_DEBUG,
CRL_R_MAX * 4);
- sysbus_init_mmio(sbd, &s->reg_array->mem);
+ xvcb->regs = s->regs;
+ sysbus_init_mmio(sbd, &xvcb->reg_array->mem);
sysbus_init_irq(sbd, &s->irq);
- for (i = 0; i < ARRAY_SIZE(s->cfg.cpu_r5); ++i) {
- object_property_add_link(obj, "cpu_r5[*]", TYPE_ARM_CPU,
- (Object **)&s->cfg.cpu_r5[i],
+ for (i = 0; i < ARRAY_SIZE(s->cfg.rpu); ++i) {
+ object_property_add_link(obj, "rpu[*]", TYPE_ARM_CPU,
+ (Object **)&s->cfg.rpu[i],
qdev_prop_allow_set_link_before_realize,
OBJ_PROP_LINK_STRONG);
}
@@ -372,19 +675,88 @@ static void crl_init(Object *obj)
OBJ_PROP_LINK_STRONG);
}
- object_property_add_link(obj, "usb", TYPE_DEVICE,
- (Object **)&s->cfg.gem[i],
- qdev_prop_allow_set_link_before_realize,
- OBJ_PROP_LINK_STRONG);
+ for (i = 0; i < ARRAY_SIZE(s->cfg.usb); ++i) {
+ object_property_add_link(obj, "usb[*]", TYPE_DEVICE,
+ (Object **)&s->cfg.usb[i],
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_STRONG);
+ }
+}
+
+static void versal2_crl_init(Object *obj)
+{
+ XlnxVersal2CRL *s = XLNX_VERSAL2_CRL(obj);
+ XlnxVersalCRLBase *xvcb = XLNX_VERSAL_CRL_BASE(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ size_t i;
+
+ xvcb->reg_array = register_init_block32(DEVICE(obj), versal2_crl_regs_info,
+ ARRAY_SIZE(versal2_crl_regs_info),
+ s->regs_info, s->regs,
+ &crl_ops,
+ XLNX_VERSAL_CRL_ERR_DEBUG,
+ VERSAL2_CRL_R_MAX * 4);
+ xvcb->regs = s->regs;
+
+ sysbus_init_mmio(sbd, &xvcb->reg_array->mem);
+
+ for (i = 0; i < ARRAY_SIZE(s->cfg.rpu); ++i) {
+ object_property_add_link(obj, "rpu[*]", TYPE_ARM_CPU,
+ (Object **)&s->cfg.rpu[i],
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_STRONG);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(s->cfg.adma); ++i) {
+ object_property_add_link(obj, "adma[*]", TYPE_DEVICE,
+ (Object **)&s->cfg.adma[i],
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_STRONG);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(s->cfg.sdma); ++i) {
+ object_property_add_link(obj, "sdma[*]", TYPE_DEVICE,
+ (Object **)&s->cfg.sdma[i],
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_STRONG);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(s->cfg.uart); ++i) {
+ object_property_add_link(obj, "uart[*]", TYPE_DEVICE,
+ (Object **)&s->cfg.uart[i],
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_STRONG);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(s->cfg.gem); ++i) {
+ object_property_add_link(obj, "gem[*]", TYPE_DEVICE,
+ (Object **)&s->cfg.gem[i],
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_STRONG);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(s->cfg.usb); ++i) {
+ object_property_add_link(obj, "usb[*]", TYPE_DEVICE,
+ (Object **)&s->cfg.usb[i],
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_STRONG);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(s->cfg.can); ++i) {
+ object_property_add_link(obj, "can[*]", TYPE_DEVICE,
+ (Object **)&s->cfg.can[i],
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_STRONG);
+ }
}
static void crl_finalize(Object *obj)
{
- XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj);
+ XlnxVersalCRLBase *s = XLNX_VERSAL_CRL_BASE(obj);
register_finalize_block(s->reg_array);
}
-static const VMStateDescription vmstate_crl = {
+static const VMStateDescription vmstate_versal_crl = {
.name = TYPE_XLNX_VERSAL_CRL,
.version_id = 1,
.minimum_version_id = 1,
@@ -394,29 +766,69 @@ static const VMStateDescription vmstate_crl = {
}
};
-static void crl_class_init(ObjectClass *klass, const void *data)
+static const VMStateDescription vmstate_versal2_crl = {
+ .name = TYPE_XLNX_VERSAL2_CRL,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, XlnxVersal2CRL, VERSAL2_CRL_R_MAX),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static void versal_crl_class_init(ObjectClass *klass, const void *data)
{
- ResettableClass *rc = RESETTABLE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
+ XlnxVersalCRLBaseClass *xvcc = XLNX_VERSAL_CRL_BASE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
- dc->vmsd = &vmstate_crl;
+ dc->vmsd = &vmstate_versal_crl;
+ rc->phases.enter = versal_crl_reset_enter;
+ rc->phases.hold = versal_crl_reset_hold;
+ xvcc->decode_periph_rst = versal_decode_periph_rst;
+}
- rc->phases.enter = crl_reset_enter;
- rc->phases.hold = crl_reset_hold;
+static void versal2_crl_class_init(ObjectClass *klass, const void *data)
+{
+ XlnxVersalCRLBaseClass *xvcc = XLNX_VERSAL_CRL_BASE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ dc->vmsd = &vmstate_versal2_crl;
+ rc->phases.enter = versal2_crl_reset_enter;
+ xvcc->decode_periph_rst = versal2_decode_periph_rst;
}
-static const TypeInfo crl_info = {
- .name = TYPE_XLNX_VERSAL_CRL,
+static const TypeInfo crl_base_info = {
+ .name = TYPE_XLNX_VERSAL_CRL_BASE,
.parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(XlnxVersalCRL),
- .class_init = crl_class_init,
- .instance_init = crl_init,
+ .instance_size = sizeof(XlnxVersalCRLBase),
+ .class_size = sizeof(XlnxVersalCRLBaseClass),
.instance_finalize = crl_finalize,
+ .abstract = true,
+};
+
+static const TypeInfo versal_crl_info = {
+ .name = TYPE_XLNX_VERSAL_CRL,
+ .parent = TYPE_XLNX_VERSAL_CRL_BASE,
+ .instance_size = sizeof(XlnxVersalCRL),
+ .instance_init = versal_crl_init,
+ .class_init = versal_crl_class_init,
+};
+
+static const TypeInfo versal2_crl_info = {
+ .name = TYPE_XLNX_VERSAL2_CRL,
+ .parent = TYPE_XLNX_VERSAL_CRL_BASE,
+ .instance_size = sizeof(XlnxVersal2CRL),
+ .instance_init = versal2_crl_init,
+ .class_init = versal2_crl_class_init,
};
static void crl_register_types(void)
{
- type_register_static(&crl_info);
+ type_register_static(&crl_base_info);
+ type_register_static(&versal_crl_info);
+ type_register_static(&versal2_crl_info);
}
type_init(crl_register_types)