From 2f55058f1d782eb19aef8b88b990d59ec011ce98 Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Tue, 27 May 2025 12:12:42 -0700 Subject: [PATCH 01/22] misc/xlnx-bbram-ctrl: Removed because unused This removes the old module that has been unused since Xilinx BBRAM device accepted in upstream. Signed-off-by: Tong Ho Signed-off-by: Francisco Iglesias --- hw/misc/xlnx-bbram-ctrl.c | 619 -------------------------------------- 1 file changed, 619 deletions(-) delete mode 100644 hw/misc/xlnx-bbram-ctrl.c diff --git a/hw/misc/xlnx-bbram-ctrl.c b/hw/misc/xlnx-bbram-ctrl.c deleted file mode 100644 index 9ea1354ea0c..00000000000 --- a/hw/misc/xlnx-bbram-ctrl.c +++ /dev/null @@ -1,619 +0,0 @@ -/* - * QEMU model of the BBRAM Battery Backed RAM - * - * Copyright (c) 2014-2020 Xilinx Inc. - * - * Autogenerated by xregqemu.py 2020-02-06. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "hw/register.h" -#include "qemu/error-report.h" -#include "qemu/bitops.h" -#include "qemu/log.h" -#include "qapi/error.h" -#include "sysemu/blockdev.h" -#include "sysemu/block-backend.h" -#include "migration/vmstate.h" -#include "hw/qdev-properties.h" -#include "hw/qdev-properties-system.h" - -#include "hw/zynqmp_aes_key.h" -#include "hw/misc/xlnx-aes.h" -#include "hw/irq.h" - -#ifndef XILINX_BBRAM_CTRL_ERR_DEBUG -#define XILINX_BBRAM_CTRL_ERR_DEBUG 0 -#endif - -#define TYPE_XILINX_BBRAM_CTRL "xlnx.bbram-ctrl" - -#define XILINX_BBRAM_CTRL(obj) \ - OBJECT_CHECK(BBRAMCtrl, (obj), TYPE_XILINX_BBRAM_CTRL) - -#define DB_PRINT_L(lvl, fmt, args...) do { \ - if (XILINX_BBRAM_CTRL_ERR_DEBUG >= lvl) { \ - qemu_log("%s: " fmt, __func__, ## args); \ - } \ -} while (0); - -#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) - -REG32(BBRAM_STATUS, 0x0) - FIELD(BBRAM_STATUS, AES_CRC_PASS, 9, 1) - FIELD(BBRAM_STATUS, AES_CRC_DONE, 8, 1) - FIELD(BBRAM_STATUS, BBRAM_ZEROIZED, 4, 1) - FIELD(BBRAM_STATUS, PGM_MODE, 0, 1) -REG32(BBRAM_CTRL, 0x4) - FIELD(BBRAM_CTRL, ZEROIZE, 0, 1) -REG32(PGM_MODE, 0x8) -REG32(BBRAM_AES_CRC, 0xc) -REG32(BBRAM_0, 0x10) -REG32(BBRAM_1, 0x14) -REG32(BBRAM_2, 0x18) -REG32(BBRAM_3, 0x1c) -REG32(BBRAM_4, 0x20) -REG32(BBRAM_5, 0x24) -REG32(BBRAM_6, 0x28) -REG32(BBRAM_7, 0x2c) -REG32(BBRAM_8, 0x30) -REG32(BBRAM_SLVERR, 0x34) - FIELD(BBRAM_SLVERR, ENABLE, 0, 1) -REG32(BBRAM_ISR, 0x38) - FIELD(BBRAM_ISR, APB_SLVERR, 0, 1) -REG32(BBRAM_IMR, 0x3c) - FIELD(BBRAM_IMR, APB_SLVERR, 0, 1) -REG32(BBRAM_IER, 0x40) - FIELD(BBRAM_IER, APB_SLVERR, 0, 1) -REG32(BBRAM_IDR, 0x44) - FIELD(BBRAM_IDR, APB_SLVERR, 0, 1) -REG32(BBRAM_MSW_LOCK, 0x4c) - FIELD(BBRAM_MSW_LOCK, VAL, 0, 1) - -#define R_MAX (R_BBRAM_MSW_LOCK + 1) - -#define RAM_MAX (A_BBRAM_8 + 4 - A_BBRAM_0) - -#define ZYNQMP_BBRAM_SIZE (8 * 4) -#define ZYNQMP_PGM_MAGIC 0x757bdf0d -#define IS_ZYNQMP (s->zynqmp_keysink) - -#define VERSAL_PGM_MAGIC 0x757BDF0F - -typedef struct BBRAMCtrl { - SysBusDevice parent_obj; - MemoryRegion iomem; - qemu_irq irq_bbram; - - BlockBackend *blk; - ZynqMPAESKeySink *zynqmp_keysink; - uint32_t size; - uint32_t crc_zpads; - bool bbram8_wo; - bool blk_ro; - - uint32_t regs[R_MAX]; - RegisterInfo regs_info[R_MAX]; -} BBRAMCtrl; - -static bool bbram_msw_locked(BBRAMCtrl *s) -{ - return ARRAY_FIELD_EX32(s->regs, BBRAM_MSW_LOCK, VAL) != 0; -} - -static bool bbram_pgm_enabled(BBRAMCtrl *s) -{ - return ARRAY_FIELD_EX32(s->regs, BBRAM_STATUS, PGM_MODE) != 0; -} - -static void bbram_bdrv_error(BBRAMCtrl *s, int rc, gchar *detail) -{ - Error *errp; - - error_setg_errno(&errp, -rc, "%s: BBRAM backstore %s failed.", - blk_name(s->blk), detail); - error_report("%s", error_get_pretty(errp)); - error_free(errp); - - g_free(detail); -} - -static void bbram_bdrv_read(BBRAMCtrl *s) -{ - const char *prefix = object_get_canonical_path(OBJECT(s)); - uint32_t *ram = &s->regs[R_BBRAM_0]; - int nr = RAM_MAX; - int rc; - - assert(s->blk); - - s->blk_ro = !blk_supports_write_perm(s->blk); - if (!s->blk_ro) { - int rc; - - rc = blk_set_perm(s->blk, - (BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE), - BLK_PERM_ALL, NULL); - if (rc) { - s->blk_ro = true; - } - } - if (s->blk_ro) { - warn_report("%s: update not saved: backstore is read-only", prefix); - } - - rc = blk_pread(s->blk, 0, nr, ram, 0); - if (rc < 0) { - bbram_bdrv_error(s, rc, g_strdup_printf("read %u bytes", nr)); - error_setg(&error_abort, "%s: Unable to read-out contents." - "backing file too small? Expecting %u bytes", prefix, nr); - } - - if (const_le32(0x1234) != 0x1234) { - /* Convert from little-endian backstore for each 32-bit row */ - nr /= 4; - while (nr--) { - ram[nr] = le32_to_cpu(ram[nr]); - } - } -} - -static void bbram_bdrv_sync(BBRAMCtrl *s, uint64_t hwaddr) -{ - uint32_t le32; - unsigned offset; - int rc; - - assert(A_BBRAM_0 <= hwaddr && hwaddr <= A_BBRAM_8); - - /* Backstore is always in little-endian */ - le32 = cpu_to_le32(s->regs[hwaddr / 4]); - - /* Update zeroized flag */ - if (le32 && (hwaddr != A_BBRAM_8 || s->bbram8_wo)) { - ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, BBRAM_ZEROIZED, 0); - } - - if (!s->blk || s->blk_ro) { - return; - } - - offset = hwaddr - A_BBRAM_0; - rc = blk_pwrite(s->blk, offset, 4, &le32, 0); - if (rc < 0) { - bbram_bdrv_error(s, rc, g_strdup_printf("write to %u", offset)); - } -} - -static void bbram_bdrv_zero(BBRAMCtrl *s) -{ - int rc; - - ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, BBRAM_ZEROIZED, 1); - - if (!s->blk || s->blk_ro) { - return; - } - - rc = blk_make_zero(s->blk, 0); - if (rc < 0) { - bbram_bdrv_error(s, rc, g_strdup("zeroizing")); - } - - /* Restore bbram8 if it is non-zero */ - if (s->regs[R_BBRAM_8]) { - bbram_bdrv_sync(s, A_BBRAM_8); - } -} - -static void bbram_zeroize(BBRAMCtrl *s) -{ - int nr = RAM_MAX - (s->bbram8_wo ? 0 : 4); /* only wo bbram8 is cleared */ - - DB_PRINT("Zeroing out the key\n"); - memset(&s->regs[R_BBRAM_0], 0, nr); - bbram_bdrv_zero(s); -} - -static void bbram_update_irq(BBRAMCtrl *s) -{ - bool pending = s->regs[R_BBRAM_ISR] & ~s->regs[R_BBRAM_IMR]; - - DB_PRINT("Setting the interrupt: %d\n", pending); - qemu_set_irq(s->irq_bbram, pending); -} - -static void bbram_ctrl_postw(RegisterInfo *reg, uint64_t val64) -{ - BBRAMCtrl *s = XILINX_BBRAM_CTRL(reg->opaque); - uint32_t val = val64; - - if (val & R_BBRAM_CTRL_ZEROIZE_MASK) { - bbram_zeroize(s); - /* The bit is self clearing */ - s->regs[R_BBRAM_CTRL] &= ~R_BBRAM_CTRL_ZEROIZE_MASK; - } -} - -static void bbram_pgm_mode_postw(RegisterInfo *reg, uint64_t val64) -{ - BBRAMCtrl *s = XILINX_BBRAM_CTRL(reg->opaque); - uint32_t val = val64; - - switch (val) { - case ZYNQMP_PGM_MAGIC: - case VERSAL_PGM_MAGIC: - bbram_zeroize(s); - - /* The status bit is cleared only by POR */ - ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, PGM_MODE, 1); - break; - } -} - -static void bbram_aes_crc_postw(RegisterInfo *reg, uint64_t val64) -{ - BBRAMCtrl *s = XILINX_BBRAM_CTRL(reg->opaque); - uint32_t calc_crc; - - if (!bbram_pgm_enabled(s)) { - /* We are not in programming mode, don't do anything */ - return; - } - - /* Perform the AES integrity check */ - s->regs[R_BBRAM_STATUS] |= R_BBRAM_STATUS_AES_CRC_DONE_MASK; - - /* Set check status */ - calc_crc = xlnx_aes_k256_crc(&s->regs[R_BBRAM_0], s->crc_zpads); - ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, AES_CRC_PASS, - (s->regs[R_BBRAM_AES_CRC] == calc_crc)); -} - -static uint64_t bbram_key_prew(RegisterInfo *reg, uint64_t val64) -{ - BBRAMCtrl *s = XILINX_BBRAM_CTRL(reg->opaque); - uint32_t original_data = *(uint32_t *) reg->data; - - if (bbram_pgm_enabled(s)) { - DB_PRINT("Writing value: 0x%"HWADDR_PRIx"\n", val64); - return val64; - } else { - /* We are not in programming mode, don't do anything */ - qemu_log_mask(LOG_GUEST_ERROR, - "Not in programming mode, dropping the write\n"); - return original_data; - } -} - -static void bbram_aes_key_update(BBRAMCtrl *s) -{ - uint8_t end; - uint8_t i; - union { - uint8_t u8[ZYNQMP_BBRAM_SIZE]; - uint32_t u32[ZYNQMP_BBRAM_SIZE / 4]; - } key; - - end = ZYNQMP_BBRAM_SIZE / 4 - 1; - for (i = 0; i <= end; ++i) { - key.u32[end - i] = s->regs[R_BBRAM_0 + i]; - } - - zynqmp_aes_key_update(s->zynqmp_keysink, key.u8, ZYNQMP_BBRAM_SIZE); -} - -static void bbram_key_postw(RegisterInfo *reg, uint64_t val64) -{ - BBRAMCtrl *s = XILINX_BBRAM_CTRL(reg->opaque); - - bbram_bdrv_sync(s, reg->access->addr); - - if (IS_ZYNQMP) { - bbram_aes_key_update(s); - } -} - -static uint64_t bbram_wo_postr(RegisterInfo *reg, uint64_t val) -{ - return 0; -} - -static uint64_t bbram_r8_postr(RegisterInfo *reg, uint64_t val) -{ - BBRAMCtrl *s = XILINX_BBRAM_CTRL(reg->opaque); - - return s->bbram8_wo ? bbram_wo_postr(reg, val) : val; -} - -static bool bbram_r8_readonly(BBRAMCtrl *s) -{ - return !bbram_pgm_enabled(s) || bbram_msw_locked(s); -} - -static uint64_t bbram_r8_prew(RegisterInfo *reg, uint64_t val64) -{ - BBRAMCtrl *s = XILINX_BBRAM_CTRL(reg->opaque); - - if (bbram_r8_readonly(s)) { - val64 = *(uint32_t *)reg->data; - } - - return val64; -} - -static void bbram_r8_postw(RegisterInfo *reg, uint64_t val64) -{ - BBRAMCtrl *s = XILINX_BBRAM_CTRL(reg->opaque); - - if (!bbram_r8_readonly(s)) { - bbram_bdrv_sync(s, A_BBRAM_8); - } -} - -static uint64_t bbram_msw_lock_prew(RegisterInfo *reg, uint64_t val64) -{ - BBRAMCtrl *s = XILINX_BBRAM_CTRL(reg->opaque); - - /* Never lock if bbram8 is wo; and, only POR can clear the lock */ - if (s->bbram8_wo) { - val64 = 0; - } else { - val64 |= s->regs[R_BBRAM_MSW_LOCK]; - } - - return val64; -} - -static void bbram_isr_postw(RegisterInfo *reg, uint64_t val64) -{ - BBRAMCtrl *s = XILINX_BBRAM_CTRL(reg->opaque); - - bbram_update_irq(s); -} - -static uint64_t bbram_ier_prew(RegisterInfo *reg, uint64_t val64) -{ - BBRAMCtrl *s = XILINX_BBRAM_CTRL(reg->opaque); - uint32_t val = val64; - - s->regs[R_BBRAM_IMR] &= ~val; - bbram_update_irq(s); - return 0; -} - -static uint64_t bbram_idr_prew(RegisterInfo *reg, uint64_t val64) -{ - BBRAMCtrl *s = XILINX_BBRAM_CTRL(reg->opaque); - uint32_t val = val64; - - s->regs[R_BBRAM_IMR] |= val; - bbram_update_irq(s); - return 0; -} - -static RegisterAccessInfo bbram_ctrl_regs_info[] = { - { .name = "BBRAM_STATUS", .addr = A_BBRAM_STATUS, - .rsvd = 0xee, - .ro = 0x3ff, - },{ .name = "BBRAM_CTRL", .addr = A_BBRAM_CTRL, - .post_write = bbram_ctrl_postw, - },{ .name = "PGM_MODE", .addr = A_PGM_MODE, - .post_write = bbram_pgm_mode_postw, - },{ .name = "BBRAM_AES_CRC", .addr = A_BBRAM_AES_CRC, - .post_write = bbram_aes_crc_postw, - .post_read = bbram_wo_postr, - },{ .name = "BBRAM_0", .addr = A_BBRAM_0, - .pre_write = bbram_key_prew, - .post_write = bbram_key_postw, - .post_read = bbram_wo_postr, - },{ .name = "BBRAM_1", .addr = A_BBRAM_1, - .pre_write = bbram_key_prew, - .post_write = bbram_key_postw, - .post_read = bbram_wo_postr, - },{ .name = "BBRAM_2", .addr = A_BBRAM_2, - .pre_write = bbram_key_prew, - .post_write = bbram_key_postw, - .post_read = bbram_wo_postr, - },{ .name = "BBRAM_3", .addr = A_BBRAM_3, - .pre_write = bbram_key_prew, - .post_write = bbram_key_postw, - .post_read = bbram_wo_postr, - },{ .name = "BBRAM_4", .addr = A_BBRAM_4, - .pre_write = bbram_key_prew, - .post_write = bbram_key_postw, - .post_read = bbram_wo_postr, - },{ .name = "BBRAM_5", .addr = A_BBRAM_5, - .pre_write = bbram_key_prew, - .post_write = bbram_key_postw, - .post_read = bbram_wo_postr, - },{ .name = "BBRAM_6", .addr = A_BBRAM_6, - .pre_write = bbram_key_prew, - .post_write = bbram_key_postw, - .post_read = bbram_wo_postr, - },{ .name = "BBRAM_7", .addr = A_BBRAM_7, - .pre_write = bbram_key_prew, - .post_write = bbram_key_postw, - .post_read = bbram_wo_postr, - },{ .name = "BBRAM_8", .addr = A_BBRAM_8, - .pre_write = bbram_r8_prew, - .post_write = bbram_r8_postw, - .post_read = bbram_r8_postr, - },{ .name = "BBRAM_SLVERR", .addr = A_BBRAM_SLVERR, - .rsvd = ~1, - },{ .name = "BBRAM_ISR", .addr = A_BBRAM_ISR, - .w1c = 0x1, - .post_write = bbram_isr_postw, - },{ .name = "BBRAM_IMR", .addr = A_BBRAM_IMR, - .ro = 0x1, - },{ .name = "BBRAM_IER", .addr = A_BBRAM_IER, - .pre_write = bbram_ier_prew, - },{ .name = "BBRAM_IDR", .addr = A_BBRAM_IDR, - .pre_write = bbram_idr_prew, - },{ .name = "BBRAM_MSW_LOCK", .addr = A_BBRAM_MSW_LOCK, - .pre_write = bbram_msw_lock_prew, - .ro = ~R_BBRAM_MSW_LOCK_VAL_MASK, - } -}; - -static void bbram_ctrl_reset(DeviceState *dev) -{ - BBRAMCtrl *s = XILINX_BBRAM_CTRL(dev); - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { - if (i < R_BBRAM_0 || i > R_BBRAM_8) { - register_reset(&s->regs_info[i]); - } - } - - if (IS_ZYNQMP) { - bbram_aes_key_update(s); - } - - bbram_update_irq(s); -} - -static const MemoryRegionOps bbram_ctrl_ops = { - .read = register_read_memory, - .write = register_write_memory, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void bbram_ctrl_realize(DeviceState *dev, Error **errp) -{ - BBRAMCtrl *s = XILINX_BBRAM_CTRL(dev); - DriveInfo *dinfo; - BlockBackend *blk; - const char *prefix = object_get_canonical_path(OBJECT(dev)); - - if (!s->zynqmp_keysink) { - error_setg(&error_abort, - "%s: AES BBRAM key sink not connected\n", prefix); - } - - dinfo = drive_get_next(IF_PFLASH); - blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; - if (blk) { - qdev_prop_set_drive(dev, "drive", blk); - bbram_bdrv_read(s); - } -} - -static void bbram_ctrl_init(Object *obj) -{ - BBRAMCtrl *s = XILINX_BBRAM_CTRL(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - RegisterInfoArray *reg_array; - - memory_region_init(&s->iomem, obj, TYPE_XILINX_BBRAM_CTRL, R_MAX * 4); - reg_array = - register_init_block32(DEVICE(obj), bbram_ctrl_regs_info, - ARRAY_SIZE(bbram_ctrl_regs_info), - s->regs_info, s->regs, - &bbram_ctrl_ops, - XILINX_BBRAM_CTRL_ERR_DEBUG, - R_MAX * 4); - memory_region_add_subregion(&s->iomem, 0x0, ®_array->mem); - - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq_bbram); -} - -static const VMStateDescription vmstate_bbram_ctrl = { - .name = TYPE_XILINX_BBRAM_CTRL, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, BBRAMCtrl, R_MAX), - VMSTATE_END_OF_LIST(), - } -}; - -static Property bbram_ctrl_props[] = { - DEFINE_PROP_LINK("zynqmp-aes-key-sink-bbram", BBRAMCtrl, zynqmp_keysink, - TYPE_ZYNQMP_AES_KEY_SINK, ZynqMPAESKeySink *), - DEFINE_PROP_UINT32("bbram-size", BBRAMCtrl, size, ZYNQMP_BBRAM_SIZE), - DEFINE_PROP_UINT32("crc-zpads", BBRAMCtrl, crc_zpads, 1), - DEFINE_PROP_DRIVE("drive", BBRAMCtrl, blk), - DEFINE_PROP_BOOL("bbram8_wo", BBRAMCtrl, bbram8_wo, false), - DEFINE_PROP_END_OF_LIST(), -}; - -static void bbram_ctrl_erase_prop_set(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - bool do_erase = false; - - visit_type_bool(v, name, &do_erase, errp); - if (*errp) { - return; - } - - if (do_erase) { - bbram_zeroize(XILINX_BBRAM_CTRL(obj)); - } -} - -static void bbram_ctrl_erase_prop_add(ObjectClass *klass) -{ - static const char prop_name[] = "erase"; - - object_class_property_add(klass, prop_name, "bool", - NULL, /* non-gettable */ - bbram_ctrl_erase_prop_set, - NULL, /* nothing to release */ - NULL); - object_class_property_set_description(klass, prop_name, - "Set true to erase entire bbram"); -} - -static void bbram_ctrl_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = bbram_ctrl_reset; - dc->realize = bbram_ctrl_realize; - dc->vmsd = &vmstate_bbram_ctrl; - device_class_set_props(dc, bbram_ctrl_props); - bbram_ctrl_erase_prop_add(klass); -} - -static const TypeInfo bbram_ctrl_info = { - .name = TYPE_XILINX_BBRAM_CTRL, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(BBRAMCtrl), - .class_init = bbram_ctrl_class_init, - .instance_init = bbram_ctrl_init, -}; - -static void bbram_ctrl_register_types(void) -{ - type_register_static(&bbram_ctrl_info); -} - -type_init(bbram_ctrl_register_types) From 7196cd60b020bf39e6ab90647fe68c81f2ff9d18 Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Thu, 29 May 2025 08:36:51 -0700 Subject: [PATCH 02/22] hw/block/xlnx-efuse: Remove all unused files This removes, from hw/block, all Xilinx eFUSE related files, all of which have been replaced by upstream implementations in hw/nvram. Signed-off-by: Tong Ho Signed-off-by: Francisco Iglesias --- hw/block/csu_efuse.c | 952 ------------------------- hw/block/xlnx-efuse.c | 428 ----------- hw/block/xlnx-versal-pmc-efuse-cache.c | 172 ----- hw/block/xlnx-versal-pmc-efuse-ctrl.c | 847 ---------------------- hw/block/xlnx-versal-pmc-efuse.h | 30 - 5 files changed, 2429 deletions(-) delete mode 100644 hw/block/csu_efuse.c delete mode 100644 hw/block/xlnx-efuse.c delete mode 100644 hw/block/xlnx-versal-pmc-efuse-cache.c delete mode 100644 hw/block/xlnx-versal-pmc-efuse-ctrl.c delete mode 100644 hw/block/xlnx-versal-pmc-efuse.h diff --git a/hw/block/csu_efuse.c b/hw/block/csu_efuse.c deleted file mode 100644 index feb646d1a7f..00000000000 --- a/hw/block/csu_efuse.c +++ /dev/null @@ -1,952 +0,0 @@ -/* - * QEMU model of the ZynqMP eFuse - * - * Copyright (c) 2015 Xilinx Inc. - * - * Written by Edgar E. Iglesias - * Partially autogenerated by xregqemu.py 2015-01-02. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/block/flash.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "exec/address-spaces.h" -#include "qemu/host-utils.h" -#include "hw/sysbus.h" -#include "hw/register.h" -#include "hw/irq.h" -#include "sysemu/blockdev.h" -#include "qemu/bitops.h" -#include "qemu/log.h" -#include "qemu/main-loop.h" -#include "migration/vmstate.h" -#include "hw/qdev-properties.h" - -#include "sysemu/block-backend.h" -#include "hw/zynqmp_aes_key.h" -#include "hw/block/xlnx-efuse.h" - -#ifndef ZYNQMP_EFUSE_ERR_DEBUG -#define ZYNQMP_EFUSE_ERR_DEBUG 0 -#endif - -#define TYPE_ZYNQMP_EFUSE "xlnx.zynqmp-efuse" - -#define ZYNQMP_EFUSE(obj) \ - OBJECT_CHECK(ZynqMPEFuse, (obj), TYPE_ZYNQMP_EFUSE) - - -REG32(WR_LOCK, 0x0) - FIELD(WR_LOCK, LOCK, 0, 16) -REG32(CFG, 0x4) - FIELD(CFG, SLVERR_ENABLE, 5, 1) - FIELD(CFG, MARGIN_RD, 2, 2) - FIELD(CFG, PGM_EN, 1, 1) - FIELD(CFG, EFUSE_CLK_SEL, 0, 1) -REG32(STATUS, 0x8) - FIELD(STATUS, AES_CRC_PASS, 7, 1) - FIELD(STATUS, AES_CRC_DONE, 6, 1) - FIELD(STATUS, CACHE_DONE, 5, 1) - FIELD(STATUS, CACHE_LOAD, 4, 1) - FIELD(STATUS, EFUSE_3_TBIT, 2, 1) - FIELD(STATUS, EFUSE_2_TBIT, 1, 1) - FIELD(STATUS, EFUSE_0_TBIT, 0, 1) -REG32(EFUSE_PGM_ADDR, 0xc) - FIELD(EFUSE_PGM_ADDR, EFUSE, 11, 2) - FIELD(EFUSE_PGM_ADDR, ROW, 5, 6) - FIELD(EFUSE_PGM_ADDR, COLUMN, 0, 5) -REG32(EFUSE_RD_ADDR, 0x10) - FIELD(EFUSE_RD_ADDR, EFUSE, 11, 2) - FIELD(EFUSE_RD_ADDR, ROW, 5, 6) -REG32(EFUSE_RD_DATA, 0x14) -REG32(TPGM, 0x18) - FIELD(TPGM, VALUE, 0, 16) -REG32(TRD, 0x1c) - FIELD(TRD, VALUE, 0, 8) -REG32(TSU_H_PS, 0x20) - FIELD(TSU_H_PS, VALUE, 0, 8) -REG32(TSU_H_PS_CS, 0x24) - FIELD(TSU_H_PS_CS, VALUE, 0, 8) -REG32(TSU_H_CS, 0x2c) - FIELD(TSU_H_CS, VALUE, 0, 4) -REG32(EFUSE_ISR, 0x30) - FIELD(EFUSE_ISR, APB_SLVERR, 31, 1) - FIELD(EFUSE_ISR, CACHE_ERROR, 4, 1) - FIELD(EFUSE_ISR, RD_ERROR, 3, 1) - FIELD(EFUSE_ISR, RD_DONE, 2, 1) - FIELD(EFUSE_ISR, PGM_ERROR, 1, 1) - FIELD(EFUSE_ISR, PGM_DONE, 0, 1) -REG32(EFUSE_IMR, 0x34) - FIELD(EFUSE_IMR, APB_SLVERR, 31, 1) - FIELD(EFUSE_IMR, CACHE_ERROR, 4, 1) - FIELD(EFUSE_IMR, RD_ERROR, 3, 1) - FIELD(EFUSE_IMR, RD_DONE, 2, 1) - FIELD(EFUSE_IMR, PGM_ERROR, 1, 1) - FIELD(EFUSE_IMR, PGM_DONE, 0, 1) -REG32(EFUSE_IER, 0x38) - FIELD(EFUSE_IER, APB_SLVERR, 31, 1) - FIELD(EFUSE_IER, CACHE_ERROR, 4, 1) - FIELD(EFUSE_IER, RD_ERROR, 3, 1) - FIELD(EFUSE_IER, RD_DONE, 2, 1) - FIELD(EFUSE_IER, PGM_ERROR, 1, 1) - FIELD(EFUSE_IER, PGM_DONE, 0, 1) -REG32(EFUSE_IDR, 0x3c) - FIELD(EFUSE_IDR, APB_SLVERR, 31, 1) - FIELD(EFUSE_IDR, CACHE_ERROR, 4, 1) - FIELD(EFUSE_IDR, RD_ERROR, 3, 1) - FIELD(EFUSE_IDR, RD_DONE, 2, 1) - FIELD(EFUSE_IDR, PGM_ERROR, 1, 1) - FIELD(EFUSE_IDR, PGM_DONE, 0, 1) -REG32(EFUSE_CACHE_LOAD, 0x40) - FIELD(EFUSE_CACHE_LOAD, LOAD, 0, 1) -REG32(EFUSE_PGM_LOCK, 0x44) - FIELD(EFUSE_PGM_LOCK, SPK_ID_LOCK, 0, 1) -REG32(EFUSE_AES_CRC, 0x48) -REG32(EFUSE_TBITS_PRGRMG_EN, 0x100) - FIELD(EFUSE_TBITS_PRGRMG_EN, TBITS_PRGRMG_EN, 3, 1) -REG32(DNA_0, 0x100c) -REG32(DNA_1, 0x1010) -REG32(DNA_2, 0x1014) -REG32(IPDISABLE, 0x1018) - FIELD(IPDISABLE, VCU_DIS, 8, 1) - FIELD(IPDISABLE, GPU_DIS, 5, 1) - FIELD(IPDISABLE, APU3_DIS, 3, 1) - FIELD(IPDISABLE, APU2_DIS, 2, 1) - FIELD(IPDISABLE, APU1_DIS, 1, 1) - FIELD(IPDISABLE, APU0_DIS, 0, 1) -REG32(SYSOSC_CTRL, 0x101c) - FIELD(SYSOSC_CTRL, SYSOSC_EN, 0, 1) -REG32(USER_0, 0x1020) -REG32(USER_1, 0x1024) -REG32(USER_2, 0x1028) -REG32(USER_3, 0x102c) -REG32(USER_4, 0x1030) -REG32(USER_5, 0x1034) -REG32(USER_6, 0x1038) -REG32(USER_7, 0x103c) -REG32(MISC_USER_CTRL, 0x1040) - FIELD(MISC_USER_CTRL, FPD_SC_EN_0, 14, 1) - FIELD(MISC_USER_CTRL, LPD_SC_EN_0, 11, 1) - FIELD(MISC_USER_CTRL, LBIST_EN, 10, 1) - FIELD(MISC_USER_CTRL, USR_WRLK_7, 7, 1) - FIELD(MISC_USER_CTRL, USR_WRLK_6, 6, 1) - FIELD(MISC_USER_CTRL, USR_WRLK_5, 5, 1) - FIELD(MISC_USER_CTRL, USR_WRLK_4, 4, 1) - FIELD(MISC_USER_CTRL, USR_WRLK_3, 3, 1) - FIELD(MISC_USER_CTRL, USR_WRLK_2, 2, 1) - FIELD(MISC_USER_CTRL, USR_WRLK_1, 1, 1) - FIELD(MISC_USER_CTRL, USR_WRLK_0, 0, 1) -REG32(ROM_RSVD, 0x1044) - FIELD(ROM_RSVD, PBR_BOOT_ERROR, 0, 3) -REG32(PUF_CHASH, 0x1050) -REG32(PUF_MISC, 0x1054) - FIELD(PUF_MISC, REGISTER_DIS, 31, 1) - FIELD(PUF_MISC, SYN_WRLK, 30, 1) - FIELD(PUF_MISC, SYN_INVLD, 29, 1) - FIELD(PUF_MISC, TEST2_DIS, 28, 1) - FIELD(PUF_MISC, UNUSED27, 27, 1) - FIELD(PUF_MISC, UNUSED26, 26, 1) - FIELD(PUF_MISC, UNUSED25, 25, 1) - FIELD(PUF_MISC, UNUSED24, 24, 1) - FIELD(PUF_MISC, AUX, 0, 24) -REG32(SEC_CTRL, 0x1058) - FIELD(SEC_CTRL, PPK1_INVLD, 30, 2) - FIELD(SEC_CTRL, PPK1_WRLK, 29, 1) - FIELD(SEC_CTRL, PPK0_INVLD, 27, 2) - FIELD(SEC_CTRL, PPK0_WRLK, 26, 1) - FIELD(SEC_CTRL, RSA_EN, 11, 15) - FIELD(SEC_CTRL, SEC_LOCK, 10, 1) - FIELD(SEC_CTRL, PROG_GATE_2, 9, 1) - FIELD(SEC_CTRL, PROG_GATE_1, 8, 1) - FIELD(SEC_CTRL, PROG_GATE_0, 7, 1) - FIELD(SEC_CTRL, DFT_DIS, 6, 1) - FIELD(SEC_CTRL, JTAG_DIS, 5, 1) - FIELD(SEC_CTRL, ERROR_DIS, 4, 1) - FIELD(SEC_CTRL, BBRAM_DIS, 3, 1) - FIELD(SEC_CTRL, ENC_ONLY, 2, 1) - FIELD(SEC_CTRL, AES_WRLK, 1, 1) - FIELD(SEC_CTRL, AES_RDLK, 0, 1) -REG32(SPK_ID, 0x105c) -REG32(PPK0_0, 0x10a0) -REG32(PPK0_1, 0x10a4) -REG32(PPK0_2, 0x10a8) -REG32(PPK0_3, 0x10ac) -REG32(PPK0_4, 0x10b0) -REG32(PPK0_5, 0x10b4) -REG32(PPK0_6, 0x10b8) -REG32(PPK0_7, 0x10bc) -REG32(PPK0_8, 0x10c0) -REG32(PPK0_9, 0x10c4) -REG32(PPK0_10, 0x10c8) -REG32(PPK0_11, 0x10cc) -REG32(PPK1_0, 0x10d0) -REG32(PPK1_1, 0x10d4) -REG32(PPK1_2, 0x10d8) -REG32(PPK1_3, 0x10dc) -REG32(PPK1_4, 0x10e0) -REG32(PPK1_5, 0x10e4) -REG32(PPK1_6, 0x10e8) -REG32(PPK1_7, 0x10ec) -REG32(PPK1_8, 0x10f0) -REG32(PPK1_9, 0x10f4) -REG32(PPK1_10, 0x10f8) -REG32(PPK1_11, 0x10fc) - -#define BIT_POS(ROW, COLUMN) (ROW * 32 + COLUMN) -#define R_MAX (R_PPK1_11 + 1) - -/* #define EFUSE_XOSC 26 */ - -/* - * eFUSE layout references: - * ZynqMP: UG1085 (v2.1) August 21, 2019, p.277, Table 12-13 - */ -#define EFUSE_AES_RDLK BIT_POS(22, 0) -#define EFUSE_AES_WRLK BIT_POS(22, 1) -#define EFUSE_ENC_ONLY BIT_POS(22, 2) -#define EFUSE_BBRAM_DIS BIT_POS(22, 3) -#define EFUSE_ERROR_DIS BIT_POS(22, 4) -#define EFUSE_JTAG_DIS BIT_POS(22, 5) -#define EFUSE_DFT_DIS BIT_POS(22, 6) -#define EFUSE_PROG_GATE_0 BIT_POS(22, 7) -#define EFUSE_PROG_GATE_1 BIT_POS(22, 7) -#define EFUSE_PROG_GATE_2 BIT_POS(22, 9) -#define EFUSE_SEC_LOCK BIT_POS(22, 10) -#define EFUSE_RSA_EN BIT_POS(22, 11) -#define EFUSE_RSA_EN14 BIT_POS(22, 25) -#define EFUSE_PPK0_WRLK BIT_POS(22, 26) -#define EFUSE_PPK0_INVLD BIT_POS(22, 27) -#define EFUSE_PPK0_INVLD_1 BIT_POS(22, 28) -#define EFUSE_PPK1_WRLK BIT_POS(22, 29) -#define EFUSE_PPK1_INVLD BIT_POS(22, 30) -#define EFUSE_PPK1_INVLD_1 BIT_POS(22, 31) - -/* Areas. */ -#define EFUSE_TRIM_START BIT_POS(1, 0) -#define EFUSE_TRIM_END BIT_POS(1, 30) -#define EFUSE_DNA_START BIT_POS(3, 0) -#define EFUSE_DNA_END BIT_POS(5, 31) -#define EFUSE_AES_START BIT_POS(24, 0) -#define EFUSE_AES_END BIT_POS(31, 31) -#define EFUSE_ROM_START BIT_POS(17, 0) -#define EFUSE_ROM_END BIT_POS(17, 31) -#define EFUSE_IPDIS_START BIT_POS(6, 0) -#define EFUSE_IPDIS_END BIT_POS(6, 31) -#define EFUSE_USER_START BIT_POS(8, 0) -#define EFUSE_USER_END BIT_POS(15, 31) -#define EFUSE_BISR_START BIT_POS(32, 0) -#define EFUSE_BISR_END BIT_POS(39, 31) - -#define EFUSE_USER_CTRL_START BIT_POS(16, 0) -#define EFUSE_USER_CTRL_END BIT_POS(16, 16) -#define EFUSE_USER_CTRL_MASK ((uint32_t)MAKE_64BIT_MASK(0, 17)) - -#define EFUSE_PUF_CHASH_START BIT_POS(20, 0) -#define EFUSE_PUF_CHASH_END BIT_POS(20, 31) -#define EFUSE_PUF_MISC_START BIT_POS(21, 0) -#define EFUSE_PUF_MISC_END BIT_POS(21, 31) -#define EFUSE_PUF_SYN_WRLK BIT_POS(21, 30) - -#define EFUSE_SPK_START BIT_POS(23, 0) -#define EFUSE_SPK_END BIT_POS(23, 31) - -#define EFUSE_PPK0_START BIT_POS(40, 0) -#define EFUSE_PPK0_END BIT_POS(51, 31) -#define EFUSE_PPK1_START BIT_POS(52, 0) -#define EFUSE_PPK1_END BIT_POS(63, 31) - -typedef struct ZynqMPEFuse { - SysBusDevice parent_obj; - MemoryRegion iomem; - qemu_irq irq; - - ZynqMPAESKeySink *key_sink; - - XLNXEFuse *efuse; - void (*refresh_cache)(struct ZynqMPEFuse *, unsigned int); - uint32_t regs[R_MAX]; - RegisterInfo regs_info[R_MAX]; -} ZynqMPEFuse; - -#define EFUSE_CACHE_FLD(s, reg, field) \ - ARRAY_FIELD_DP32((s)->regs, reg, field, \ - (efuse_get_row((s->efuse), EFUSE_ ## field) \ - >> (EFUSE_ ## field % 32))) - -#define EFUSE_CACHE_BIT(s, reg, field) \ - ARRAY_FIELD_DP32((s)->regs, reg, field, efuse_get_bit((s->efuse), \ - EFUSE_ ## field)) - -static void update_tbit_status(ZynqMPEFuse *s) -{ - unsigned int check = efuse_tbits_check(s->efuse); - uint32_t val = s->regs[R_STATUS]; - - val = FIELD_DP32(val, STATUS, EFUSE_0_TBIT, !!(check & (1 << 0))); - val = FIELD_DP32(val, STATUS, EFUSE_2_TBIT, !!(check & (1 << 1))); - val = FIELD_DP32(val, STATUS, EFUSE_3_TBIT, !!(check & (1 << 2))); - - s->regs[R_STATUS] = val; -} - -/* - * Keep the syncs in bit order so we can bail out for the - * slower ones. - */ -static void zynqmp_efuse_sync_cache(ZynqMPEFuse *s, unsigned int bit) -{ - EFUSE_CACHE_BIT(s, SEC_CTRL, AES_RDLK); - EFUSE_CACHE_BIT(s, SEC_CTRL, AES_WRLK); - EFUSE_CACHE_BIT(s, SEC_CTRL, ENC_ONLY); - EFUSE_CACHE_BIT(s, SEC_CTRL, BBRAM_DIS); - EFUSE_CACHE_BIT(s, SEC_CTRL, ERROR_DIS); - EFUSE_CACHE_BIT(s, SEC_CTRL, JTAG_DIS); - EFUSE_CACHE_BIT(s, SEC_CTRL, DFT_DIS); - EFUSE_CACHE_BIT(s, SEC_CTRL, PROG_GATE_0); - EFUSE_CACHE_BIT(s, SEC_CTRL, PROG_GATE_1); - EFUSE_CACHE_BIT(s, SEC_CTRL, PROG_GATE_2); - EFUSE_CACHE_BIT(s, SEC_CTRL, SEC_LOCK); - EFUSE_CACHE_BIT(s, SEC_CTRL, PPK0_WRLK); - EFUSE_CACHE_BIT(s, SEC_CTRL, PPK1_WRLK); - - EFUSE_CACHE_FLD(s, SEC_CTRL, RSA_EN); - EFUSE_CACHE_FLD(s, SEC_CTRL, PPK0_INVLD); - EFUSE_CACHE_FLD(s, SEC_CTRL, PPK1_INVLD); - - /* Update the tbits. */ - update_tbit_status(s); - - /* Sync the various areas. */ - s->regs[R_MISC_USER_CTRL] = efuse_get_row(s->efuse, EFUSE_USER_CTRL_START) - & EFUSE_USER_CTRL_MASK; - s->regs[R_PUF_CHASH] = efuse_get_row(s->efuse, EFUSE_PUF_CHASH_START); - s->regs[R_PUF_MISC] = efuse_get_row(s->efuse, EFUSE_PUF_MISC_START); - - efuse_sync_u32(s->efuse, &s->regs[R_DNA_0], EFUSE_DNA_START, - EFUSE_DNA_END, bit); - - if (bit < EFUSE_AES_START) { - return; - } - - /* Sync the AES Key. */ - efuse_k256_sync(s->efuse, s->key_sink, EFUSE_AES_START); - - efuse_sync_u32(s->efuse, &s->regs[R_ROM_RSVD], - EFUSE_ROM_START, EFUSE_ROM_END, bit); - efuse_sync_u32(s->efuse, &s->regs[R_IPDISABLE], - EFUSE_IPDIS_START, EFUSE_IPDIS_END, bit); - efuse_sync_u32(s->efuse, &s->regs[R_USER_0], - EFUSE_USER_START, EFUSE_USER_END, bit); - efuse_sync_u32(s->efuse, &s->regs[R_SPK_ID], - EFUSE_SPK_START, EFUSE_SPK_END, bit); - efuse_sync_u32(s->efuse, &s->regs[R_PPK0_0], - EFUSE_PPK0_START, EFUSE_PPK0_END, bit); - efuse_sync_u32(s->efuse, &s->regs[R_PPK1_0], - EFUSE_PPK1_START, EFUSE_PPK1_END, bit); -} - -static void zynqmp_efuse_update_irq(ZynqMPEFuse *s) -{ - bool pending = s->regs[R_EFUSE_ISR] & s->regs[R_EFUSE_IMR]; - qemu_set_irq(s->irq, pending); -} - -static void zynqmp_efuse_isr_postw(RegisterInfo *reg, uint64_t val64) -{ - ZynqMPEFuse *s = ZYNQMP_EFUSE(reg->opaque); - zynqmp_efuse_update_irq(s); -} - -static uint64_t zynqmp_efuse_ier_prew(RegisterInfo *reg, uint64_t val64) -{ - ZynqMPEFuse *s = ZYNQMP_EFUSE(reg->opaque); - uint32_t val = val64; - - s->regs[R_EFUSE_IMR] |= val; - zynqmp_efuse_update_irq(s); - return 0; -} - -static uint64_t zynqmp_efuse_idr_prew(RegisterInfo *reg, uint64_t val64) -{ - ZynqMPEFuse *s = ZYNQMP_EFUSE(reg->opaque); - uint32_t val = val64; - - s->regs[R_EFUSE_IMR] &= ~val; - zynqmp_efuse_update_irq(s); - return 0; -} - -static bool zynqmp_efuse_idx_is_aes_key(unsigned int fuse) -{ - if (fuse >= EFUSE_AES_START && fuse <= EFUSE_AES_END) { - return true; - } - return false; -} - -static void zynqmp_efuse_pgm_addr_postw(RegisterInfo *reg, uint64_t val64) -{ - ZynqMPEFuse *s = ZYNQMP_EFUSE(reg->opaque); - bool puf_prot = false; - bool wr_lock, ps, prot; - unsigned int efuse_idx; - - efuse_idx = s->regs[R_EFUSE_PGM_ADDR]; - - /* Allow only valid array, and adjust for skipped array 1 */ - switch (ARRAY_FIELD_EX32(s->regs, EFUSE_PGM_ADDR, EFUSE)) { - case 0: - break; - case 2: - puf_prot = efuse_get_bit(s->efuse, EFUSE_PUF_SYN_WRLK); - val64 = FIELD_DP32(efuse_idx, EFUSE_PGM_ADDR, EFUSE, 1); - break; - case 3: - puf_prot = efuse_get_bit(s->efuse, EFUSE_PUF_SYN_WRLK); - val64 = FIELD_DP32(efuse_idx, EFUSE_PGM_ADDR, EFUSE, 2); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Denied write to invalid eFuse array: addr=%#x\n", - object_get_canonical_path(OBJECT(s)), - efuse_idx); - return; - } - - /* TODO: Sanity check the timing setup. */ - - wr_lock = ARRAY_FIELD_EX32(s->regs, WR_LOCK, LOCK); - if (wr_lock) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Write to eFuse %u while write protected\n", - object_get_canonical_path(OBJECT(s)), - efuse_idx); - return; - } - - ps = ARRAY_FIELD_EX32(s->regs, CFG, PGM_EN); - if (!ps) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Write to eFuse %u while not powered\n", - object_get_canonical_path(OBJECT(s)), - efuse_idx); - return; - } - - if (puf_prot) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Write to eFuse PUF_HD page while write-locked\n", - object_get_canonical_path(OBJECT(s))); - return; - } - - /* I'm not sure if this is HW enforced or not. */ - prot = ARRAY_FIELD_EX32(s->regs, SEC_CTRL, AES_WRLK); - if (prot && zynqmp_efuse_idx_is_aes_key(efuse_idx)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Write to AES key eFuse %u while not wr protected\n", - object_get_canonical_path(OBJECT(s)), - efuse_idx); - return; - } - - /* Set up to detect done-transition */ - ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, PGM_ERROR, 0); - ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, PGM_DONE, 0); - - efuse_pgm_start(s->efuse, 13, val64); - /* To Vefify after programming, Cache reload should should happen or - * Margin Read is done - * FIXME: Implement Margin Read - */ -} - -static void zynqmp_efuse_pgm_done(DeviceState *dev, bool failed) -{ - ZynqMPEFuse *s = ZYNQMP_EFUSE(dev); - - ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, PGM_ERROR, (failed ? 1 : 0)); - ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, PGM_DONE, 1); - zynqmp_efuse_update_irq(s); -} - -static void zynqmp_efuse_rd_addr_postw(RegisterInfo *reg, uint64_t val64) -{ - ZynqMPEFuse *s = ZYNQMP_EFUSE(reg->opaque); - - /* - * zynqmp_efuse_mem_read() has already applied in-programming guard. - * - * Still need to grant reads only to allowed bits; reference sources: - * 1/ XilSKey - XilSKey_ZynqMp_EfusePs_ReadRow() - * 2/ UG1085, v2.0, table 12-13 - * (note: enumerates the masks as per described in - * references to avoid mental translation). - */ -#define COL_MASK(L_, H_) \ - ((uint32_t)MAKE_64BIT_MASK((L_), (1 + (H_) - (L_)))) - - static const uint32_t ary0_col_mask[] = { - /* XilSKey - XSK_ZYNQMP_EFUSEPS_TBITS_ROW */ - [0] = COL_MASK(28, 31), - - /* XilSKey - XSK_ZYNQMP_EFUSEPS_USR{0:7}_FUSE_ROW */ - [8] = COL_MASK(0, 31), [9] = COL_MASK(0, 31), - [10] = COL_MASK(0, 31), [11] = COL_MASK(0, 31), - [12] = COL_MASK(0, 31), [13] = COL_MASK(0, 31), - [14] = COL_MASK(0, 31), [15] = COL_MASK(0, 31), - - /* XilSKey - XSK_ZYNQMP_EFUSEPS_MISC_USR_CTRL_ROW */ - [16] = COL_MASK(0, 7) | COL_MASK(10, 16), - - /* XilSKey - XSK_ZYNQMP_EFUSEPS_PBR_BOOT_ERR_ROW */ - [17] = COL_MASK(0, 2), - - /* XilSKey - XSK_ZYNQMP_EFUSEPS_PUF_CHASH_ROW */ - [20] = COL_MASK(0, 31), - - /* XilSKey - XSK_ZYNQMP_EFUSEPS_PUF_AUX_ROW */ - [21] = COL_MASK(0, 23) | COL_MASK(29, 31), - - /* XilSKey - XSK_ZYNQMP_EFUSEPS_SEC_CTRL_ROW */ - [22] = COL_MASK(0, 31), - - /* XilSKey - XSK_ZYNQMP_EFUSEPS_SPK_ID_ROW */ - [23] = COL_MASK(0, 31), - - /* XilSKey - XSK_ZYNQMP_EFUSEPS_PPK0_START_ROW */ - [40] = COL_MASK(0, 31), [41] = COL_MASK(0, 31), - [42] = COL_MASK(0, 31), [43] = COL_MASK(0, 31), - [44] = COL_MASK(0, 31), [45] = COL_MASK(0, 31), - [46] = COL_MASK(0, 31), [47] = COL_MASK(0, 31), - [48] = COL_MASK(0, 31), [49] = COL_MASK(0, 31), - [50] = COL_MASK(0, 31), [51] = COL_MASK(0, 31), - - /* XilSKey - XSK_ZYNQMP_EFUSEPS_PPK1_START_ROW */ - [52] = COL_MASK(0, 31), [53] = COL_MASK(0, 31), - [54] = COL_MASK(0, 31), [55] = COL_MASK(0, 31), - [56] = COL_MASK(0, 31), [57] = COL_MASK(0, 31), - [58] = COL_MASK(0, 31), [59] = COL_MASK(0, 31), - [60] = COL_MASK(0, 31), [61] = COL_MASK(0, 31), - [62] = COL_MASK(0, 31), [63] = COL_MASK(0, 31), - }; - - uint32_t col_mask = COL_MASK(0, 31); -#undef COL_MASK - - uint32_t efuse_idx = s->regs[R_EFUSE_RD_ADDR]; - uint32_t efuse_ary = FIELD_EX32(efuse_idx, EFUSE_RD_ADDR, EFUSE); - uint32_t efuse_row = FIELD_EX32(efuse_idx, EFUSE_RD_ADDR, ROW); - - switch (efuse_ary) { - case 0: /* Various */ - if (efuse_row >= ARRAY_SIZE(ary0_col_mask)) { - goto denied; - } - - col_mask = ary0_col_mask[efuse_row]; - if (!col_mask) { - goto denied; - } - break; - case 2: /* PUF helper data, adjust for skipped array 1 */ - case 3: - val64 = FIELD_DP32(efuse_idx, EFUSE_RD_ADDR, EFUSE, efuse_ary - 1); - break; - default: - goto denied; - } - - s->regs[R_EFUSE_RD_DATA] = efuse_get_row(s->efuse, val64) & col_mask; - - ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, RD_ERROR, 0); - ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, RD_DONE, 1); - zynqmp_efuse_update_irq(s); - return; - - denied: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Denied efuse read from array %u, row %u\n", - object_get_canonical_path(OBJECT(s)), - efuse_ary, efuse_row); - - s->regs[R_EFUSE_RD_DATA] = 0; - - ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, RD_ERROR, 1); - ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, RD_DONE, 0); - zynqmp_efuse_update_irq(s); -} - -static void zynqmp_efuse_aes_crc_postw(RegisterInfo *reg, uint64_t val64) -{ - ZynqMPEFuse *s = ZYNQMP_EFUSE(reg->opaque); - bool ok; - - ok = efuse_k256_check(s->efuse, (uint32_t)val64, EFUSE_AES_START); - - ARRAY_FIELD_DP32(s->regs, STATUS, AES_CRC_PASS, (ok ? 1 : 0)); - ARRAY_FIELD_DP32(s->regs, STATUS, AES_CRC_DONE, 1); -} - -static void zynqmp_efuse_cfg_postw(RegisterInfo *reg, uint64_t val64) -{ - ZynqMPEFuse *s = ZYNQMP_EFUSE(reg->opaque); - unsigned int ps; - uint32_t val32 = val64; - - ps = FIELD_EX32(val32, CFG, PGM_EN); - efuse_stop_timer_ps(s->efuse); - if (ps) { - efuse_set_timer_ps(s->efuse, 500); - } -} - -static uint64_t zynqmp_efuse_cache_load_prew(RegisterInfo *reg, - uint64_t valu64) -{ - ZynqMPEFuse *s = ZYNQMP_EFUSE(reg->opaque); - - if (valu64 & R_EFUSE_CACHE_LOAD_LOAD_MASK) { - s->refresh_cache(s, FBIT_UNKNOWN); - ARRAY_FIELD_DP32(s->regs, STATUS, CACHE_DONE, 1); - zynqmp_efuse_update_irq(s); - } - - return 0; -} - -static uint64_t zynqmp_wr_lock_prew(RegisterInfo *reg, uint64_t val) -{ - return val == 0xDF0D ? 0 : 1; -} - -static RegisterAccessInfo zynqmp_efuse_regs_info[] = { - { .name = "WR_LOCK", .addr = A_WR_LOCK, - .reset = 0x1, - .pre_write = zynqmp_wr_lock_prew, - },{ .name = "CFG", .addr = A_CFG, - .post_write = zynqmp_efuse_cfg_postw - },{ .name = "STATUS", .addr = A_STATUS, - .rsvd = 0x8, - .ro = 0xff, - },{ .name = "EFUSE_PGM_ADDR", .addr = A_EFUSE_PGM_ADDR, - .post_write = zynqmp_efuse_pgm_addr_postw - },{ .name = "EFUSE_RD_ADDR", .addr = A_EFUSE_RD_ADDR, - .rsvd = 0x1f, - .post_write = zynqmp_efuse_rd_addr_postw, - },{ .name = "EFUSE_RD_DATA", .addr = A_EFUSE_RD_DATA, - .ro = 0xffffffff, - },{ .name = "TPGM", .addr = A_TPGM, - },{ .name = "TRD", .addr = A_TRD, - .reset = 0x1b, - },{ .name = "TSU_H_PS", .addr = A_TSU_H_PS, - .reset = 0xff, - },{ .name = "TSU_H_PS_CS", .addr = A_TSU_H_PS_CS, - .reset = 0xb, - },{ .name = "TSU_H_CS", .addr = A_TSU_H_CS, - .reset = 0x7, - },{ .name = "EFUSE_ISR", .addr = A_EFUSE_ISR, - .rsvd = 0x7fffffe0, - .w1c = 0x8000001f, - .post_write = zynqmp_efuse_isr_postw, - },{ .name = "EFUSE_IMR", .addr = A_EFUSE_IMR, - .reset = 0x8000001f, - .rsvd = 0x7fffffe0, - .ro = 0xffffffff, - },{ .name = "EFUSE_IER", .addr = A_EFUSE_IER, - .rsvd = 0x7fffffe0, - .pre_write = zynqmp_efuse_ier_prew, - },{ .name = "EFUSE_IDR", .addr = A_EFUSE_IDR, - .rsvd = 0x7fffffe0, - .pre_write = zynqmp_efuse_idr_prew, - },{ .name = "EFUSE_CACHE_LOAD", .addr = A_EFUSE_CACHE_LOAD, - .pre_write = zynqmp_efuse_cache_load_prew, - },{ .name = "EFUSE_PGM_LOCK", .addr = A_EFUSE_PGM_LOCK, - },{ .name = "EFUSE_AES_CRC", .addr = A_EFUSE_AES_CRC, - .post_write = zynqmp_efuse_aes_crc_postw, - },{ .name = "EFUSE_TBITS_PRGRMG_EN", .addr = A_EFUSE_TBITS_PRGRMG_EN, - .reset = R_EFUSE_TBITS_PRGRMG_EN_TBITS_PRGRMG_EN_MASK, - },{ .name = "DNA_0", .addr = A_DNA_0, - .ro = 0xffffffff, - },{ .name = "DNA_1", .addr = A_DNA_1, - .ro = 0xffffffff, - },{ .name = "DNA_2", .addr = A_DNA_2, - .ro = 0xffffffff, - },{ .name = "IPDISABLE", .addr = A_IPDISABLE, - .ro = 0xffffffff, - },{ .name = "SYSOSC_CTRL", .addr = A_SYSOSC_CTRL, - .ro = 0xffffffff, - },{ .name = "USER_0", .addr = A_USER_0, - .ro = 0xffffffff, - },{ .name = "USER_1", .addr = A_USER_1, - .ro = 0xffffffff, - },{ .name = "USER_2", .addr = A_USER_2, - .ro = 0xffffffff, - },{ .name = "USER_3", .addr = A_USER_3, - .ro = 0xffffffff, - },{ .name = "USER_4", .addr = A_USER_4, - .ro = 0xffffffff, - },{ .name = "USER_5", .addr = A_USER_5, - .ro = 0xffffffff, - },{ .name = "USER_6", .addr = A_USER_6, - .ro = 0xffffffff, - },{ .name = "USER_7", .addr = A_USER_7, - .ro = 0xffffffff, - },{ .name = "MISC_USER_CTRL", .addr = A_MISC_USER_CTRL, - .ro = 0xffffffff, - },{ .name = "ROM_RSVD", .addr = A_ROM_RSVD, - .ro = 0xffffffff, - },{ .name = "PUF_CHASH", .addr = A_PUF_CHASH, - .ro = 0xffffffff, - },{ .name = "PUF_MISC", .addr = A_PUF_MISC, - .ro = 0xffffffff, - },{ .name = "SEC_CTRL", .addr = A_SEC_CTRL, - .ro = 0xffffffff, - },{ .name = "SPK_ID", .addr = A_SPK_ID, - .ro = 0xffffffff, - },{ .name = "PPK0_0", .addr = A_PPK0_0, - .ro = 0xffffffff, - },{ .name = "PPK0_1", .addr = A_PPK0_1, - .ro = 0xffffffff, - },{ .name = "PPK0_2", .addr = A_PPK0_2, - .ro = 0xffffffff, - },{ .name = "PPK0_3", .addr = A_PPK0_3, - .ro = 0xffffffff, - },{ .name = "PPK0_4", .addr = A_PPK0_4, - .ro = 0xffffffff, - },{ .name = "PPK0_5", .addr = A_PPK0_5, - .ro = 0xffffffff, - },{ .name = "PPK0_6", .addr = A_PPK0_6, - .ro = 0xffffffff, - },{ .name = "PPK0_7", .addr = A_PPK0_7, - .ro = 0xffffffff, - },{ .name = "PPK0_8", .addr = A_PPK0_8, - .ro = 0xffffffff, - },{ .name = "PPK0_9", .addr = A_PPK0_9, - .ro = 0xffffffff, - },{ .name = "PPK0_10", .addr = A_PPK0_10, - .ro = 0xffffffff, - },{ .name = "PPK0_11", .addr = A_PPK0_11, - .ro = 0xffffffff, - },{ .name = "PPK1_0", .addr = A_PPK1_0, - .ro = 0xffffffff, - },{ .name = "PPK1_1", .addr = A_PPK1_1, - .ro = 0xffffffff, - },{ .name = "PPK1_2", .addr = A_PPK1_2, - .ro = 0xffffffff, - },{ .name = "PPK1_3", .addr = A_PPK1_3, - .ro = 0xffffffff, - },{ .name = "PPK1_4", .addr = A_PPK1_4, - .ro = 0xffffffff, - },{ .name = "PPK1_5", .addr = A_PPK1_5, - .ro = 0xffffffff, - },{ .name = "PPK1_6", .addr = A_PPK1_6, - .ro = 0xffffffff, - },{ .name = "PPK1_7", .addr = A_PPK1_7, - .ro = 0xffffffff, - },{ .name = "PPK1_8", .addr = A_PPK1_8, - .ro = 0xffffffff, - },{ .name = "PPK1_9", .addr = A_PPK1_9, - .ro = 0xffffffff, - },{ .name = "PPK1_10", .addr = A_PPK1_10, - .ro = 0xffffffff, - },{ .name = "PPK1_11", .addr = A_PPK1_11, - .ro = 0xffffffff, - } -}; - -/* Return dev-obj from reg-region created by register_init_block32 */ -static ZynqMPEFuse *zynqmp_efuse_of_mr(void *mr_accessor) -{ - RegisterInfoArray *reg_array = mr_accessor; - Object *dev; - - assert(reg_array != NULL); - - dev = reg_array->mem.owner; - assert(dev); - - return ZYNQMP_EFUSE(dev); -} - -static uint64_t zynqmp_efuse_mem_read(void *opaque, hwaddr addr, unsigned size) -{ - ZynqMPEFuse *s = zynqmp_efuse_of_mr(opaque); - - /* Process programming completion if late. */ - efuse_pgm_complete(s->efuse); - - if (efuse_is_pgm(s->efuse)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Accessing device while programming\n", - object_get_canonical_path(OBJECT(s))); - } - - return register_read_memory(opaque, addr, size); -} - -static MemTxResult zynqmp_efuse_mem_write_with_attrs(void *opaque, hwaddr addr, - uint64_t v64, unsigned size, MemTxAttrs attrs) -{ - ZynqMPEFuse *s = zynqmp_efuse_of_mr(opaque); - - /* Process programming completion if late. */ - efuse_pgm_complete(s->efuse); - - if (efuse_is_pgm(s->efuse)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Accessing device while programming\n", - object_get_canonical_path(OBJECT(s))); - } - - return register_write_memory_with_attrs(opaque, addr, v64, size, attrs); -} - -static const MemoryRegionOps zynqmp_efuse_ops = { - .read = zynqmp_efuse_mem_read, - .write_with_attrs = zynqmp_efuse_mem_write_with_attrs, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void zynqmp_efuse_register_reset(RegisterInfo *reg) -{ - if (!reg->data || !reg->access) { - return; - } - - /* Reset must not trigger some registers' writers */ - switch (reg->access->addr) { - case A_EFUSE_AES_CRC: - *(uint32_t *)reg->data = reg->access->reset; - return; - } - - register_reset(reg); -} - -static void zynqmp_efuse_reset(DeviceState *dev) -{ - ZynqMPEFuse *s = ZYNQMP_EFUSE(dev); - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { - zynqmp_efuse_register_reset(&s->regs_info[i]); - } - - s->refresh_cache(s, FBIT_UNKNOWN); - ARRAY_FIELD_DP32(s->regs, STATUS, CACHE_DONE, 1); - zynqmp_efuse_update_irq(s); -} - -static void zynqmp_efuse_realize(DeviceState *dev, Error **errp) -{ - ZynqMPEFuse *s = ZYNQMP_EFUSE(dev); - const char *prefix = object_get_canonical_path(OBJECT(dev)); - - if (!s->key_sink) { - error_setg(&error_abort, - "%s: AES EFUSE key sink not connected\n", prefix); - } - - if (!s->efuse) { - error_setg(&error_abort, "%s: XLN-EFUSE not connected", prefix); - } - s->efuse->pgm_done = zynqmp_efuse_pgm_done; - s->efuse->dev = dev; -} - -static void zynqmp_efuse_init(Object *obj) -{ - ZynqMPEFuse *s = ZYNQMP_EFUSE(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - RegisterInfoArray *reg_array; - - memory_region_init(&s->iomem, obj, TYPE_ZYNQMP_EFUSE, R_MAX * 4); - reg_array = - register_init_block32(DEVICE(obj), zynqmp_efuse_regs_info, - ARRAY_SIZE(zynqmp_efuse_regs_info), - s->regs_info, s->regs, - &zynqmp_efuse_ops, - ZYNQMP_EFUSE_ERR_DEBUG, - R_MAX * 4); - memory_region_add_subregion(&s->iomem, 0, ®_array->mem); - - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - - s->refresh_cache = zynqmp_efuse_sync_cache; -} - -static const VMStateDescription vmstate_efuse = { - .name = TYPE_ZYNQMP_EFUSE, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, ZynqMPEFuse, R_MAX), - VMSTATE_END_OF_LIST(), - } -}; - -static Property zynqmp_efuse_props[] = { - DEFINE_PROP_LINK("efuse", - ZynqMPEFuse, efuse, - TYPE_XLNX_EFUSE, XLNXEFuse *), - - DEFINE_PROP_LINK("zynqmp-aes-key-sink-efuses", - ZynqMPEFuse, key_sink, - TYPE_ZYNQMP_AES_KEY_SINK, ZynqMPAESKeySink *), - - DEFINE_PROP_END_OF_LIST(), -}; - -static void zynqmp_efuse_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = zynqmp_efuse_reset; - dc->realize = zynqmp_efuse_realize; - dc->vmsd = &vmstate_efuse; - device_class_set_props(dc, zynqmp_efuse_props); -} - - -static const TypeInfo efuse_info = { - .name = TYPE_ZYNQMP_EFUSE, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ZynqMPEFuse), - .class_init = zynqmp_efuse_class_init, - .instance_init = zynqmp_efuse_init, -}; - -static void efuse_register_types(void) -{ - type_register_static(&efuse_info); -} - -type_init(efuse_register_types) diff --git a/hw/block/xlnx-efuse.c b/hw/block/xlnx-efuse.c deleted file mode 100644 index 95fbbb1601f..00000000000 --- a/hw/block/xlnx-efuse.c +++ /dev/null @@ -1,428 +0,0 @@ -/* - * QEMU model of the EFUSE eFuse - * - * Copyright (c) 2015 Xilinx Inc. - * - * Written by Edgar E. Iglesias - * Partially autogenerated by xregqemu.py 2015-01-02. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/block/flash.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "sysemu/block-backend.h" -#include "exec/address-spaces.h" -#include "qemu/host-utils.h" -#include "hw/sysbus.h" -#include "hw/ptimer.h" -#include "sysemu/blockdev.h" -#include "qemu/bitops.h" -#include "qemu/error-report.h" -#include "qemu/log.h" -#include "qemu/main-loop.h" -#include "migration/vmstate.h" -#include "hw/qdev-properties.h" -#include "hw/qdev-properties-system.h" - -#include "sysemu/block-backend.h" -#include "hw/zynqmp_aes_key.h" -#include "hw/block/xlnx-efuse.h" -#include "hw/misc/xlnx-aes.h" - -#ifndef XLNX_EFUSE_ERR_DEBUG -#define XLNX_EFUSE_ERR_DEBUG 0 -#endif - -#define TBIT0_OFFSET 28 -#define TBIT1_OFFSET 29 -#define TBIT2_OFFSET 30 -#define TBIT3_OFFSET 31 -#define TBITS_PATTERN (0x0AU << TBIT0_OFFSET) -#define TBITS_MASK (0x0FU << TBIT0_OFFSET) - -bool efuse_get_bit(XLNXEFuse *s, unsigned int bit) -{ - bool b = s->fuse32[bit / 32] & (1 << (bit % 32)); - return b; -} - -bool efuse_is_pgm(XLNXEFuse *s) -{ - return s->programming; -} - -#ifdef BIT_TEST -static bool efuse_bit_is_test(unsigned int fbit) -{ - unsigned int word = fbit / 32; - unsigned int lword = word % 32; - unsigned int bit = fbit % 32; - bool tst = false; - - if (lword == 0 && bit < 4) { - tst = true; - } else if (word == 23) { - if (bit >= 23) { - tst = true; - } - } else if (word < 23 || word > 31) { - - if (lword == bit) { - tst = true; - } - } - - return tst; -} -#endif - -/* Update the u32 array from efuse bits. Slow but simple approach. */ -void efuse_sync_u32(XLNXEFuse *s, uint32_t *u32, - unsigned int f_start, unsigned int f_end, - unsigned int f_written) -{ - unsigned int fbit, wbits = 0, u32_off = 0; - - /* Avoid working on bits that are not relevant. */ - if (f_written != FBIT_UNKNOWN - && (f_written < f_start || f_written > f_end)) { - return; - } - - for (fbit = f_start; fbit <= f_end; fbit++, wbits++) { -#ifdef BIT_TEST - if (efuse_bit_is_test(fbit)) { - continue; - } -#endif - if (wbits == 32) { - /* Update the key offset. */ - u32_off += 1; - wbits = 0; - } - u32[u32_off] |= efuse_get_bit(s, fbit) << wbits; - } -} - -static void efuse_sync_bdrv(XLNXEFuse *s, unsigned int bit) -{ - const int bswap_adj = (const_le32(0x1234) != 0x1234 ? 3 : 0); - unsigned int efuse_byte; - - if (!s->blk || s->blk_ro) { - return; /* Silient on read-only backend to avoid message flood */ - } - - efuse_byte = bit / 8; - - if (blk_pwrite(s->blk, efuse_byte, 1, - ((uint8_t *) s->fuse32) + (efuse_byte ^ bswap_adj), - 0) < 0) { - error_report("%s: write error in byte %" PRIu32 ".", - __func__, efuse_byte); - } -} - -static int efuse_ro_bits_cmp(const void *a, const void *b) -{ - uint32_t i = *(const uint32_t *)a; - uint32_t j = *(const uint32_t *)b; - - return (i > j) - (i < j); -} - -static void efuse_ro_bits_sort(XLNXEFuse *s) -{ - uint32_t *ary = s->ro_bits; - const uint32_t cnt = s->ro_bits_cnt; - - if (ary && cnt > 1) { - qsort(ary, cnt, sizeof(ary[0]), efuse_ro_bits_cmp); - } -} - -static bool efuse_ro_bits_find(XLNXEFuse *s, uint32_t k) -{ - const uint32_t *ary = s->ro_bits; - const uint32_t cnt = s->ro_bits_cnt; - - if (!ary || !cnt) { - return false; - } - - return bsearch(&k, ary, cnt, sizeof(ary[0]), efuse_ro_bits_cmp) != NULL; -} - -bool efuse_set_bit(XLNXEFuse *s, unsigned int bit) -{ - uint32_t set, *row; - - if (efuse_ro_bits_find(s, bit)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: WARN: " - "Ignored setting of readonly efuse bit<%u,%u>!\n", - object_get_canonical_path(OBJECT(s)), - (bit / 32), (bit % 32)); - return false; - } - - /* Avoid back-end write unless there is a real update */ - row = &s->fuse32[bit / 32]; - set = 1 << (bit % 32); - if (!(set & *row)) { - *row |= set; - efuse_sync_bdrv(s, bit); - } - return true; -} - -void efuse_pgm_start(XLNXEFuse *s, int tpgm, uint64_t val) -{ - s->efuse_idx = (uint32_t) val; - ptimer_transaction_begin(s->timer_pgm); - ptimer_stop(s->timer_pgm); - /* HW specs say 12us (+- 1us). Real value to be specified when - real sillicon arrives. We emulate 13us per fuse, 26us with the - redundancy bits. */ - ptimer_set_limit(s->timer_pgm, tpgm * 2, 1); - ptimer_run(s->timer_pgm, 1); - ptimer_transaction_commit(s->timer_pgm); - s->programming = true; -} - -static void timer_ps_hit(void *opaque) -{ - XLNXEFuse *s = XLNX_EFUSE(opaque); - qemu_log_mask(LOG_GUEST_ERROR, - "%s: FATAL: XLNXEFuse PS held high for more than 500ms!\n", - object_get_canonical_path(OBJECT(s))); -} - -static void timer_pgm_hit(void *opaque) -{ - XLNXEFuse *s = XLNX_EFUSE(opaque); - bool ok; - - if (s->programming == false) { - return; /* false alarm */ - } - - ok = efuse_set_bit(s, s->efuse_idx); - s->programming = false; - - if (s->pgm_done) { - s->pgm_done(s->dev, !ok); - } -} - -void efuse_stop_timer_ps(XLNXEFuse *s) -{ - ptimer_transaction_begin(s->timer_ps); - ptimer_stop(s->timer_ps); - ptimer_transaction_commit(s->timer_ps); -} - -void efuse_set_timer_ps(XLNXEFuse *s, int tsu_h_ps) -{ - ptimer_transaction_begin(s->timer_ps); - ptimer_stop(s->timer_ps); - ptimer_set_limit(s->timer_ps, tsu_h_ps * 1000, 1); - ptimer_run(s->timer_ps, 1); - ptimer_transaction_commit(s->timer_ps); -} - -void efuse_pgm_complete(XLNXEFuse *s) -{ - if (s->programming && ptimer_get_count(s->timer_pgm) == 0) { - /* Programming is ready. */ - timer_pgm_hit(s); - } -} - -bool efuse_k256_check(XLNXEFuse *s, uint32_t crc, unsigned start) -{ - /* A key always occupies multiple of whole rows */ - assert((start % 32) == 0); - - return crc == xlnx_aes_k256_crc(&s->fuse32[start / 32], 0); -} - -void efuse_k256_sync(XLNXEFuse *s, ZynqMPAESKeySink *sink, unsigned start) -{ - union { - uint8_t u8[256 / 8]; - uint32_t u32[256 / 32]; - } key; - - const unsigned last = ARRAY_SIZE(key.u32) - 1; - unsigned nr; - - /* A key always occupies multiple of whole rows */ - assert((start % 32) == 0); - - if (!sink) { - return; - } - - start /= 32; - for (nr = 0; nr <= last; nr++) { - key.u32[last - nr] = s->fuse32[start + nr]; - } - - zynqmp_aes_key_update(sink, key.u8, sizeof(key.u8)); -} - -uint32_t efuse_tbits_check(XLNXEFuse *s) -{ - int nr; - uint32_t check = 0; - - for (nr = s->efuse_nr; nr-- > 0; ) { - int efuse_start_row_num = (s->efuse_size * nr) / 32; - uint32_t data = s->fuse32[efuse_start_row_num]; - - /* - * If the option is on, auto-init blank T-bits. - * (non-blank will still be reported as '0' in the check, e.g., - * for error-injection tests) - */ - if ((data & TBITS_MASK) == 0 && s->init_tbits) { - data |= TBITS_PATTERN; - - s->fuse32[efuse_start_row_num] = data; - efuse_sync_bdrv(s, (efuse_start_row_num * 32 + TBIT0_OFFSET)); - } - - check = (check << 1) | ((data & TBITS_MASK) == TBITS_PATTERN); - } - - return check; -} - -static void efuse_realize(DeviceState *dev, Error **errp) -{ - XLNXEFuse *s = XLNX_EFUSE(dev); - BlockBackend *blk; - DriveInfo *dinfo; - unsigned int nr_bytes; - const char *prefix = object_get_canonical_path(OBJECT(dev)); - - /* Temporary hack until migration to -blockdev */ - dinfo = drive_get_by_index(IF_PFLASH, - s->efuse_size >= 8192 ? 1 : 3); - blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; - - nr_bytes = ROUND_UP((s->efuse_nr * s->efuse_size) / 8, 4); - s->fuse32 = g_malloc0(nr_bytes); - if (blk) { - qdev_prop_set_drive(dev, "drive", blk); - - s->blk_ro = !blk_supports_write_perm(s->blk); - if (!s->blk_ro) { - int rc; - - rc = blk_set_perm(s->blk, - (BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE), - BLK_PERM_ALL, NULL); - if (rc) { - s->blk_ro = true; - } - } - if (s->blk_ro) { - warn_report("%s: update not saved: backstore is read-only", - object_get_canonical_path(OBJECT(s))); - } - if (blk_pread(s->blk, 0, nr_bytes, (void *) s->fuse32, 0) < 0) { - error_setg(&error_abort, "%s: Unable to read-out contents." - "backing file too small? Expecting %" PRIu32" bytes", - prefix, - (unsigned int) (nr_bytes)); - } - if (const_le32(0x1234) != 0x1234) { - /* Convert from little-endian backstore for each 32-bit row */ - unsigned int nr_u32; - - for (nr_u32 = 0; nr_u32 < (nr_bytes / 4); nr_u32++) { - s->fuse32[nr_u32] = le32_to_cpu(s->fuse32[nr_u32]); - } - } - } - - s->timer_ps = ptimer_init(timer_ps_hit, s, PTIMER_POLICY_LEGACY); - s->timer_pgm = ptimer_init(timer_pgm_hit, s, PTIMER_POLICY_LEGACY); - - /* Micro-seconds. */ - ptimer_transaction_begin(s->timer_ps); - ptimer_set_freq(s->timer_ps, 1000 * 1000); - ptimer_transaction_commit(s->timer_ps); - - ptimer_transaction_begin(s->timer_pgm); - ptimer_set_freq(s->timer_pgm, 1000 * 1000); - ptimer_transaction_commit(s->timer_pgm); - - /* Sort readonly-list for bsearch lookup */ - efuse_ro_bits_sort(s); -} - -static Property efuse_properties[] = { - DEFINE_PROP_UINT8("efuse-nr", XLNXEFuse, efuse_nr, 3), - DEFINE_PROP_UINT32("efuse-size", XLNXEFuse, efuse_size, 64 * 32), - DEFINE_PROP_DRIVE("drive", XLNXEFuse, blk), - DEFINE_PROP_BOOL("init-factory-tbits", XLNXEFuse, init_tbits, true), - DEFINE_PROP_ARRAY("read-only", XLNXEFuse, ro_bits_cnt, ro_bits, - qdev_prop_uint32, uint32_t), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vmstate_efuse = { - .name = TYPE_XLNX_EFUSE, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_BOOL(programming, XLNXEFuse), - VMSTATE_PTIMER(timer_ps, XLNXEFuse), - VMSTATE_PTIMER(timer_ps, XLNXEFuse), - VMSTATE_END_OF_LIST(), - } -}; - -static void efuse_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = efuse_realize; - dc->vmsd = &vmstate_efuse; - device_class_set_props(dc, efuse_properties); -} - -static const TypeInfo efuse_info = { - .name = TYPE_XLNX_EFUSE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(XLNXEFuse), - .class_init = efuse_class_init, -}; - -static void efuse_register_types(void) -{ - type_register_static(&efuse_info); -} -type_init(efuse_register_types) diff --git a/hw/block/xlnx-versal-pmc-efuse-cache.c b/hw/block/xlnx-versal-pmc-efuse-cache.c deleted file mode 100644 index 393586fd765..00000000000 --- a/hw/block/xlnx-versal-pmc-efuse-cache.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * QEMU model of the EFuse_Cache - * - * Copyright (c) 2017 Xilinx Inc. - * - * Partially generated by xregqemu.py 2017-06-05. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "qemu/bitops.h" -#include "qemu/log.h" -#include "migration/vmstate.h" -#include "hw/qdev-properties.h" -#include "xlnx-versal-pmc-efuse.h" - -#ifndef XILINX_EFUSE_CACHE_ERR_DEBUG -#define XILINX_EFUSE_CACHE_ERR_DEBUG 0 -#endif - -#define DPRINT(...) \ - if (XILINX_EFUSE_CACHE_ERR_DEBUG) { \ - qemu_log(__VA_ARGS__); \ - } - -#define TYPE_XILINX_EFUSE_CACHE "xlnx.pmc-efuse-cache" - -#define XILINX_EFUSE_CACHE(obj) \ - OBJECT_CHECK(EFuseCache, (obj), TYPE_XILINX_EFUSE_CACHE) - -#define DPRINT_GE(args, ...) \ - qemu_log_mask(LOG_GUEST_ERROR, "%s: " args, __func__, ## __VA_ARGS__); - -#define R_MAX 0x1000 - -typedef struct EFuseCache { - SysBusDevice parent_obj; - MemoryRegion iomem; - - XLNXEFuse *efuse; -} EFuseCache; - -static uint64_t efuse_cache_read(void *opaque, hwaddr addr, unsigned size) -{ - EFuseCache *s = XILINX_EFUSE_CACHE(opaque); - unsigned int w0 = QEMU_ALIGN_DOWN(addr * 8, 32); - unsigned int w1 = QEMU_ALIGN_DOWN((addr + size - 1) * 8, 32); - - uint64_t ret; - - assert(w0 == w1 || (w0 + 32) == w1); - - ret = versal_efuse_read_row(s->efuse, w1, NULL); - if (w0 < w1) { - ret <<= 32; - ret |= versal_efuse_read_row(s->efuse, w0, NULL); - } - - /* If 'addr' unaligned, the guest is always assumed to be little-endian. */ - addr &= 3; - if (addr) { - ret >>= 8 * addr; - } - - return ret; -} - -static void efuse_cache_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - /* No Register Writes allowed */ - DPRINT_GE("Invalid write to efuse cache registers"); -} - -static const MemoryRegionOps efuse_cache_ops = { - .read = efuse_cache_read, - .write = efuse_cache_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 4, - }, -}; - -static void efuse_cache_init(Object *obj) -{ - EFuseCache *s = XILINX_EFUSE_CACHE(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - memory_region_init_io(&s->iomem, obj, &efuse_cache_ops, s, - TYPE_XILINX_EFUSE_CACHE, R_MAX); - sysbus_init_mmio(sbd, &s->iomem); - object_property_add_link(obj, "efuse", TYPE_XLNX_EFUSE, - (Object **)&s->efuse, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_STRONG); -} - -static const VMStateDescription vmstate_efuse_cache = { - .name = TYPE_XILINX_EFUSE_CACHE, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_END_OF_LIST(), - } -}; - -static void efuse_cache_sysmon_data_source(Object *obj, - XlnxEFuseSysmonData *data) -{ - EFuseCache *s = XILINX_EFUSE_CACHE(obj); - - assert(data); - memset(data, 0, sizeof(*data)); - - if (!s->efuse) { - return; - } - - /* Must provide data without access screening */ - data->rdata_low = efuse_get_row(s->efuse, (59 * 32)); - data->rdata_high = efuse_get_row(s->efuse, (60 * 32)); - data->glitch_monitor_en = efuse_get_bit(s->efuse, (40 * 32 + 29)); -} - -static void efuse_cache_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - XlnxEFuseSysmonDataSourceClass *esdc; - - dc->vmsd = &vmstate_efuse_cache; - - esdc = XLNX_EFUSE_SYSMON_DATA_SOURCE_CLASS(klass); - esdc->get_data = efuse_cache_sysmon_data_source; -} - -static const TypeInfo efuse_cache_info = { - .name = TYPE_XILINX_EFUSE_CACHE, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(EFuseCache), - .class_init = efuse_cache_class_init, - .instance_init = efuse_cache_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_XLNX_EFUSE_SYSMON_DATA_SOURCE }, - { } - } -}; - -static void efuse_cache_register_types(void) -{ - type_register_static(&efuse_cache_info); -} - -type_init(efuse_cache_register_types) diff --git a/hw/block/xlnx-versal-pmc-efuse-ctrl.c b/hw/block/xlnx-versal-pmc-efuse-ctrl.c deleted file mode 100644 index f5d964f1bd5..00000000000 --- a/hw/block/xlnx-versal-pmc-efuse-ctrl.c +++ /dev/null @@ -1,847 +0,0 @@ -/* - * QEMU model of the Versal eFuse controller - * - * Copyright (c) 2020 Xilinx Inc. - * - * Autogenerated by xregqemu.py 2020-01-16. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" - -#include "hw/sysbus.h" -#include "hw/register.h" -#include "qemu/bitops.h" -#include "qemu/log.h" -#include "migration/vmstate.h" -#include "hw/irq.h" -#include "qapi/error.h" -#include "hw/qdev-properties.h" -#include "hw/zynqmp_aes_key.h" -#include "xlnx-versal-pmc-efuse.h" - -#ifndef XILINX_EFUSE_CTRL_ERR_DEBUG -#define XILINX_EFUSE_CTRL_ERR_DEBUG 0 -#endif - -#define TYPE_XILINX_EFUSE_CTRL "xlnx.versal-efuse" - -#define XILINX_EFUSE_CTRL(obj) \ - OBJECT_CHECK(EFUSE_CTRL, (obj), TYPE_XILINX_EFUSE_CTRL) - -REG32(WR_LOCK, 0x0) - FIELD(WR_LOCK, LOCK, 0, 16) -REG32(CFG, 0x4) - FIELD(CFG, SLVERR_ENABLE, 5, 1) - FIELD(CFG, MARGIN_RD, 2, 1) - FIELD(CFG, PGM_EN, 1, 1) -REG32(STATUS, 0x8) - FIELD(STATUS, AES_USER_KEY_1_CRC_PASS, 11, 1) - FIELD(STATUS, AES_USER_KEY_1_CRC_DONE, 10, 1) - FIELD(STATUS, AES_USER_KEY_0_CRC_PASS, 9, 1) - FIELD(STATUS, AES_USER_KEY_0_CRC_DONE, 8, 1) - FIELD(STATUS, AES_CRC_PASS, 7, 1) - FIELD(STATUS, AES_CRC_DONE, 6, 1) - FIELD(STATUS, CACHE_DONE, 5, 1) - FIELD(STATUS, CACHE_LOAD, 4, 1) - FIELD(STATUS, EFUSE_2_TBIT, 2, 1) - FIELD(STATUS, EFUSE_1_TBIT, 1, 1) - FIELD(STATUS, EFUSE_0_TBIT, 0, 1) -REG32(EFUSE_PGM_ADDR, 0xc) - FIELD(EFUSE_PGM_ADDR, PAGE, 13, 4) - FIELD(EFUSE_PGM_ADDR, ROW, 5, 8) - FIELD(EFUSE_PGM_ADDR, COLUMN, 0, 5) -REG32(EFUSE_RD_ADDR, 0x10) - FIELD(EFUSE_RD_ADDR, PAGE, 13, 4) - FIELD(EFUSE_RD_ADDR, ROW, 5, 8) -REG32(EFUSE_RD_DATA, 0x14) -REG32(TPGM, 0x18) - FIELD(TPGM, VALUE, 0, 16) -REG32(TRD, 0x1c) - FIELD(TRD, VALUE, 0, 8) -REG32(TSU_H_PS, 0x20) - FIELD(TSU_H_PS, VALUE, 0, 8) -REG32(TSU_H_PS_CS, 0x24) - FIELD(TSU_H_PS_CS, VALUE, 0, 8) -REG32(TRDM, 0x28) - FIELD(TRDM, VALUE, 0, 8) -REG32(TSU_H_CS, 0x2c) - FIELD(TSU_H_CS, VALUE, 0, 8) -REG32(EFUSE_ISR, 0x30) - FIELD(EFUSE_ISR, APB_SLVERR, 31, 1) - FIELD(EFUSE_ISR, CACHE_PARITY_E2, 14, 1) - FIELD(EFUSE_ISR, CACHE_PARITY_E1, 13, 1) - FIELD(EFUSE_ISR, CACHE_PARITY_E0S, 12, 1) - FIELD(EFUSE_ISR, CACHE_PARITY_E0R, 11, 1) - FIELD(EFUSE_ISR, CACHE_APB_SLVERR, 10, 1) - FIELD(EFUSE_ISR, CACHE_REQ_ERROR, 9, 1) - FIELD(EFUSE_ISR, MAIN_REQ_ERROR, 8, 1) - FIELD(EFUSE_ISR, READ_ON_CACHE_LD, 7, 1) - FIELD(EFUSE_ISR, CACHE_FSM_ERROR, 6, 1) - FIELD(EFUSE_ISR, MAIN_FSM_ERROR, 5, 1) - FIELD(EFUSE_ISR, CACHE_ERROR, 4, 1) - FIELD(EFUSE_ISR, RD_ERROR, 3, 1) - FIELD(EFUSE_ISR, RD_DONE, 2, 1) - FIELD(EFUSE_ISR, PGM_ERROR, 1, 1) - FIELD(EFUSE_ISR, PGM_DONE, 0, 1) -REG32(EFUSE_IMR, 0x34) - FIELD(EFUSE_IMR, APB_SLVERR, 31, 1) - FIELD(EFUSE_IMR, CACHE_PARITY_E2, 14, 1) - FIELD(EFUSE_IMR, CACHE_PARITY_E1, 13, 1) - FIELD(EFUSE_IMR, CACHE_PARITY_E0S, 12, 1) - FIELD(EFUSE_IMR, CACHE_PARITY_E0R, 11, 1) - FIELD(EFUSE_IMR, CACHE_APB_SLVERR, 10, 1) - FIELD(EFUSE_IMR, CACHE_REQ_ERROR, 9, 1) - FIELD(EFUSE_IMR, MAIN_REQ_ERROR, 8, 1) - FIELD(EFUSE_IMR, READ_ON_CACHE_LD, 7, 1) - FIELD(EFUSE_IMR, CACHE_FSM_ERROR, 6, 1) - FIELD(EFUSE_IMR, MAIN_FSM_ERROR, 5, 1) - FIELD(EFUSE_IMR, CACHE_ERROR, 4, 1) - FIELD(EFUSE_IMR, RD_ERROR, 3, 1) - FIELD(EFUSE_IMR, RD_DONE, 2, 1) - FIELD(EFUSE_IMR, PGM_ERROR, 1, 1) - FIELD(EFUSE_IMR, PGM_DONE, 0, 1) -REG32(EFUSE_IER, 0x38) - FIELD(EFUSE_IER, APB_SLVERR, 31, 1) - FIELD(EFUSE_IER, CACHE_PARITY_E2, 14, 1) - FIELD(EFUSE_IER, CACHE_PARITY_E1, 13, 1) - FIELD(EFUSE_IER, CACHE_PARITY_E0S, 12, 1) - FIELD(EFUSE_IER, CACHE_PARITY_E0R, 11, 1) - FIELD(EFUSE_IER, CACHE_APB_SLVERR, 10, 1) - FIELD(EFUSE_IER, CACHE_REQ_ERROR, 9, 1) - FIELD(EFUSE_IER, MAIN_REQ_ERROR, 8, 1) - FIELD(EFUSE_IER, READ_ON_CACHE_LD, 7, 1) - FIELD(EFUSE_IER, CACHE_FSM_ERROR, 6, 1) - FIELD(EFUSE_IER, MAIN_FSM_ERROR, 5, 1) - FIELD(EFUSE_IER, CACHE_ERROR, 4, 1) - FIELD(EFUSE_IER, RD_ERROR, 3, 1) - FIELD(EFUSE_IER, RD_DONE, 2, 1) - FIELD(EFUSE_IER, PGM_ERROR, 1, 1) - FIELD(EFUSE_IER, PGM_DONE, 0, 1) -REG32(EFUSE_IDR, 0x3c) - FIELD(EFUSE_IDR, APB_SLVERR, 31, 1) - FIELD(EFUSE_IDR, CACHE_PARITY_E2, 14, 1) - FIELD(EFUSE_IDR, CACHE_PARITY_E1, 13, 1) - FIELD(EFUSE_IDR, CACHE_PARITY_E0S, 12, 1) - FIELD(EFUSE_IDR, CACHE_PARITY_E0R, 11, 1) - FIELD(EFUSE_IDR, CACHE_APB_SLVERR, 10, 1) - FIELD(EFUSE_IDR, CACHE_REQ_ERROR, 9, 1) - FIELD(EFUSE_IDR, MAIN_REQ_ERROR, 8, 1) - FIELD(EFUSE_IDR, READ_ON_CACHE_LD, 7, 1) - FIELD(EFUSE_IDR, CACHE_FSM_ERROR, 6, 1) - FIELD(EFUSE_IDR, MAIN_FSM_ERROR, 5, 1) - FIELD(EFUSE_IDR, CACHE_ERROR, 4, 1) - FIELD(EFUSE_IDR, RD_ERROR, 3, 1) - FIELD(EFUSE_IDR, RD_DONE, 2, 1) - FIELD(EFUSE_IDR, PGM_ERROR, 1, 1) - FIELD(EFUSE_IDR, PGM_DONE, 0, 1) -REG32(EFUSE_CACHE_LOAD, 0x40) - FIELD(EFUSE_CACHE_LOAD, LOAD, 0, 1) -REG32(EFUSE_PGM_LOCK, 0x44) - FIELD(EFUSE_PGM_LOCK, SPK_ID_LOCK, 0, 1) -REG32(EFUSE_AES_CRC, 0x48) -REG32(EFUSE_AES_USR_KEY0_CRC, 0x4c) -REG32(EFUSE_AES_USR_KEY1_CRC, 0x50) -REG32(EFUSE_PD, 0x54) -REG32(EFUSE_ANLG_OSC_SW_1LP, 0x60) -REG32(EFUSE_TEST_CTRL, 0x100) - -#define EFUSE_CTRL_R_MAX (R_EFUSE_TEST_CTRL + 1) - -#define R_WR_LOCK_UNLOCK_PASSCODE (0xDF0D) - -/* - * eFuse layout references: - * UG????, p.???, Table ??? - * https://github.com/Xilinx/embeddedsw/blob/release-2019.2/lib/sw_services/xilnvm/src/xnvm_efuse_hw.h - */ -#define BIT_POS_OF(A_) \ - ((uint32_t)((A_) & (R_EFUSE_PGM_ADDR_ROW_MASK | \ - R_EFUSE_PGM_ADDR_COLUMN_MASK))) - -#define BIT_POS(R_, C_) \ - ((uint32_t)((R_EFUSE_PGM_ADDR_ROW_MASK \ - & ((R_) << R_EFUSE_PGM_ADDR_ROW_SHIFT)) \ - | \ - (R_EFUSE_PGM_ADDR_COLUMN_MASK \ - & ((C_) << R_EFUSE_PGM_ADDR_COLUMN_SHIFT)))) - -#define EFUSE_TBIT_POS(A_) (BIT_POS_OF(A_) >= BIT_POS(0, 28)) - -#define EFUSE_ANCHOR_ROW (0) -#define EFUSE_ANCHOR_3_COL (27) -#define EFUSE_ANCHOR_1_COL (1) - -#define EFUSE_AES_KEY_START BIT_POS(12, 0) -#define EFUSE_AES_KEY_END BIT_POS(19, 31) -#define EFUSE_USER_KEY_0_START BIT_POS(20, 0) -#define EFUSE_USER_KEY_0_END BIT_POS(27, 31) -#define EFUSE_USER_KEY_1_START BIT_POS(28, 0) -#define EFUSE_USER_KEY_1_END BIT_POS(35, 31) - -#define EFUSE_RD_BLOCKED_START EFUSE_AES_KEY_START -#define EFUSE_RD_BLOCKED_END EFUSE_USER_KEY_1_END - -#define EFUSE_GLITCH_DET_WR_LK BIT_POS(4, 31) -#define EFUSE_PPK0_WR_LK BIT_POS(43, 6) -#define EFUSE_PPK1_WR_LK BIT_POS(43, 7) -#define EFUSE_PPK2_WR_LK BIT_POS(43, 8) -#define EFUSE_AES_WR_LK BIT_POS(43, 11) -#define EFUSE_USER_KEY_0_WR_LK BIT_POS(43, 13) -#define EFUSE_USER_KEY_1_WR_LK BIT_POS(43, 15) -#define EFUSE_PUF_SYN_LK BIT_POS(43, 16) -#define EFUSE_DNA_WR_LK BIT_POS(43, 27) -#define EFUSE_BOOT_ENV_WR_LK BIT_POS(43, 28) - -#define EFUSE_PGM_LOCKED_START BIT_POS(44, 0) -#define EFUSE_PGM_LOCKED_END BIT_POS(51, 31) - -#define EFUSE_PUF_PAGE (2) -#define EFUSE_PUF_SYN_START BIT_POS(129, 0) -#define EFUSE_PUF_SYN_END BIT_POS(255, 27) - -#define EFUSE_KEY_CRC_LK_ROW (43) -#define EFUSE_AES_KEY_CRC_LK_MASK ((1U << 9) | (1U << 10)) -#define EFUSE_USER_KEY_0_CRC_LK_MASK (1U << 12) -#define EFUSE_USER_KEY_1_CRC_LK_MASK (1U << 14) - -/* - * A handy macro to return value of an array element, - * or a specific default if given index is out of bound. - */ -#define ARRAY_GET(A_, I_, D_) \ - ((unsigned int)(I_) < ARRAY_SIZE(A_) ? (A_)[I_] : (D_)) - -typedef struct EFUSE_LK_SPEC { - uint16_t row; - uint16_t lk_bit; -} EFUSE_LK_SPEC; - -typedef struct EFUSE_CTRL { - SysBusDevice parent_obj; - MemoryRegion iomem; - qemu_irq irq_efuse_imr; - - XLNXEFuse *efuse; - ZynqMPAESKeySink *aes_key_sink; - ZynqMPAESKeySink *usr_key0_sink; - ZynqMPAESKeySink *usr_key1_sink; - - union { - uint16_t *u16; - EFUSE_LK_SPEC *spec; - } extra_pg0_lock; - uint32_t extra_pg0_lock_n16; - - uint32_t regs[EFUSE_CTRL_R_MAX]; - RegisterInfo regs_info[EFUSE_CTRL_R_MAX]; -} EFUSE_CTRL; - -static void efuse_imr_update_irq(EFUSE_CTRL *s) -{ - bool pending = s->regs[R_EFUSE_ISR] & ~s->regs[R_EFUSE_IMR]; - qemu_set_irq(s->irq_efuse_imr, pending); -} - -static void efuse_isr_postw(RegisterInfo *reg, uint64_t val64) -{ - EFUSE_CTRL *s = XILINX_EFUSE_CTRL(reg->opaque); - efuse_imr_update_irq(s); -} - -static uint64_t efuse_ier_prew(RegisterInfo *reg, uint64_t val64) -{ - EFUSE_CTRL *s = XILINX_EFUSE_CTRL(reg->opaque); - uint32_t val = val64; - - s->regs[R_EFUSE_IMR] &= ~val; - efuse_imr_update_irq(s); - return 0; -} - -static uint64_t efuse_idr_prew(RegisterInfo *reg, uint64_t val64) -{ - EFUSE_CTRL *s = XILINX_EFUSE_CTRL(reg->opaque); - uint32_t val = val64; - - s->regs[R_EFUSE_IMR] |= val; - efuse_imr_update_irq(s); - return 0; -} - -static void efuse_status_tbits_sync(EFUSE_CTRL *s) -{ - uint32_t check = efuse_tbits_check(s->efuse); - uint32_t val = s->regs[R_STATUS]; - - val = FIELD_DP32(val, STATUS, EFUSE_0_TBIT, !!(check & (1 << 0))); - val = FIELD_DP32(val, STATUS, EFUSE_1_TBIT, !!(check & (1 << 1))); - val = FIELD_DP32(val, STATUS, EFUSE_2_TBIT, !!(check & (1 << 2))); - - s->regs[R_STATUS] = val; -} - -static void efuse_anchor_bits_check(EFUSE_CTRL *s) -{ - unsigned page; - - if (!s->efuse || !s->efuse->init_tbits) { - return; - } - - for (page = 0; page < s->efuse->efuse_nr; page++) { - uint32_t row = 0, bit; - - row = FIELD_DP32(row, EFUSE_PGM_ADDR, PAGE, page); - row = FIELD_DP32(row, EFUSE_PGM_ADDR, ROW, EFUSE_ANCHOR_ROW); - - bit = FIELD_DP32(row, EFUSE_PGM_ADDR, COLUMN, EFUSE_ANCHOR_3_COL); - if (!efuse_get_bit(s->efuse, bit)) { - efuse_set_bit(s->efuse, bit); - } - - bit = FIELD_DP32(row, EFUSE_PGM_ADDR, COLUMN, EFUSE_ANCHOR_1_COL); - if (!efuse_get_bit(s->efuse, bit)) { - efuse_set_bit(s->efuse, bit); - } - } -} - -static void efuse_key_crc_check(RegisterInfo *reg, uint32_t crc, - uint32_t pass_mask, uint32_t done_mask, - unsigned first, uint32_t lk_mask) -{ - EFUSE_CTRL *s = XILINX_EFUSE_CTRL(reg->opaque); - uint32_t r, lk_bits; - - /* - * To start, assume both DONE and PASS, and clear PASS by xor - * if CRC-check fails or CRC-check disabled by lock fuse. - */ - r = s->regs[R_STATUS] | done_mask | pass_mask; - - lk_bits = efuse_get_row(s->efuse, EFUSE_KEY_CRC_LK_ROW) & lk_mask; - if (lk_bits == 0 && efuse_k256_check(s->efuse, crc, first)) { - pass_mask = 0; - } - - s->regs[R_STATUS] = r ^ pass_mask; -} - -static void efuse_data_sync(EFUSE_CTRL *s) -{ - efuse_status_tbits_sync(s); - efuse_k256_sync(s->efuse, s->aes_key_sink, EFUSE_AES_KEY_START); - efuse_k256_sync(s->efuse, s->usr_key0_sink, EFUSE_USER_KEY_0_START); - efuse_k256_sync(s->efuse, s->usr_key1_sink, EFUSE_USER_KEY_1_START); -} - -static int efuse_lk_spec_cmp(const void *a, const void *b) -{ - uint16_t r1 = ((const EFUSE_LK_SPEC *)a)->row; - uint16_t r2 = ((const EFUSE_LK_SPEC *)b)->row; - - return (r1 > r2) - (r1 < r2); -} - -static void efuse_lk_spec_sort(EFUSE_CTRL *s) -{ - EFUSE_LK_SPEC *ary = s->extra_pg0_lock.spec; - const uint32_t n8 = s->extra_pg0_lock_n16 * 2; - const uint32_t sz = sizeof(ary[0]); - const uint32_t cnt = n8 / sz; - - if (!ary || !n8) { - return; - } - - if ((n8 % sz) != 0) { - error_setg(&error_abort, - "%s: property 'pg0-lock' item-count not multiple of %u", - object_get_canonical_path(OBJECT(s)), sz); - } - - qsort(ary, cnt, sz, efuse_lk_spec_cmp); -} - -static uint32_t efuse_lk_spec_find(EFUSE_CTRL *s, uint32_t row) -{ - const EFUSE_LK_SPEC *ary = s->extra_pg0_lock.spec; - const uint32_t n8 = s->extra_pg0_lock_n16 * 2; - const uint32_t sz = sizeof(ary[0]); - const uint32_t cnt = n8 / sz; - const EFUSE_LK_SPEC *item = NULL; - - if (ary && cnt) { - EFUSE_LK_SPEC k = { .row = row, }; - - item = bsearch(&k, ary, cnt, sz, efuse_lk_spec_cmp); - } - - return item ? item->lk_bit : 0; -} - -static uint32_t efuse_bit_locked(EFUSE_CTRL *s, uint32_t bit) -{ - /* Hard-coded locks */ - static const uint16_t pg0_hard_lock[] = { - [4] = EFUSE_GLITCH_DET_WR_LK, - [37] = EFUSE_BOOT_ENV_WR_LK, - - [8 ... 11] = EFUSE_DNA_WR_LK, - [12 ... 19] = EFUSE_AES_WR_LK, - [20 ... 27] = EFUSE_USER_KEY_0_WR_LK, - [28 ... 35] = EFUSE_USER_KEY_1_WR_LK, - [64 ... 71] = EFUSE_PPK0_WR_LK, - [72 ... 79] = EFUSE_PPK1_WR_LK, - [80 ... 87] = EFUSE_PPK2_WR_LK, - }; - - uint32_t row = FIELD_EX32(bit, EFUSE_PGM_ADDR, ROW); - uint32_t lk_bit = ARRAY_GET(pg0_hard_lock, row, 0); - - return lk_bit ? lk_bit : efuse_lk_spec_find(s, row); -} - -static bool efuse_pgm_locked(EFUSE_CTRL *s, unsigned int bit) -{ - - unsigned int lock = 1; - - /* Global lock */ - if (!ARRAY_FIELD_EX32(s->regs, CFG, PGM_EN)) { - goto ret_lock; - } - - /* Row lock */ - switch (FIELD_EX32(bit, EFUSE_PGM_ADDR, PAGE)) { - case 0: - if (ARRAY_FIELD_EX32(s->regs, EFUSE_PGM_LOCK, SPK_ID_LOCK) && - bit >= EFUSE_PGM_LOCKED_START && bit <= EFUSE_PGM_LOCKED_END) { - goto ret_lock; - } - - lock = efuse_bit_locked(s, bit); - break; - case EFUSE_PUF_PAGE: - if (bit < EFUSE_PUF_SYN_START || bit > EFUSE_PUF_SYN_END) { - lock = 0; - goto ret_lock; - } - - lock = EFUSE_PUF_SYN_LK; - break; - default: - lock = 0; - goto ret_lock; - } - - /* Row lock by an efuse bit */ - if (lock) { - lock = efuse_get_bit(s->efuse, lock); - } - - ret_lock: - return lock != 0; -} - -static void efuse_pgm_addr_postw(RegisterInfo *reg, uint64_t val64) -{ - EFUSE_CTRL *s = XILINX_EFUSE_CTRL(reg->opaque); - unsigned bit = val64; - bool ok = false; - - /* Always zero out PGM_ADDR because it is write-only */ - s->regs[R_EFUSE_PGM_ADDR] = 0; - - /* - * Indicate error if bit is write-protected (or read-only - * as guarded by efuse_set_bit()). - * - * Keep it simple by not modeling program timing. - * - * Note: model must NEVER clear the PGM_ERROR bit; it is - * up to guest to do so (or by reset). - */ - if (efuse_pgm_locked(s, bit)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Denied setting of efuse<%u, %u, %u>\n", - object_get_canonical_path(OBJECT(s)), - FIELD_EX32(bit, EFUSE_PGM_ADDR, PAGE), - FIELD_EX32(bit, EFUSE_PGM_ADDR, ROW), - FIELD_EX32(bit, EFUSE_PGM_ADDR, COLUMN)); - } else if (efuse_set_bit(s->efuse, bit)) { - ok = true; - if (EFUSE_TBIT_POS(bit)) { - efuse_status_tbits_sync(s); - } - } - - if (!ok) { - ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, PGM_ERROR, 1); - } - - ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, PGM_DONE, 1); - efuse_imr_update_irq(s); -} - -static void efuse_rd_addr_postw(RegisterInfo *reg, uint64_t val64) -{ - EFUSE_CTRL *s = XILINX_EFUSE_CTRL(reg->opaque); - unsigned bit = val64; - bool denied; - - /* Always zero out RD_ADDR because it is write-only */ - s->regs[R_EFUSE_RD_ADDR] = 0; - - /* - * Indicate error if row is read-blocked. - * - * Note: model must NEVER clear the RD_ERROR bit; it is - * up to guest to do so (or by reset). - */ - s->regs[R_EFUSE_RD_DATA] = versal_efuse_read_row(s->efuse, bit, &denied); - if (denied) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Denied reading of efuse<%u, %u>\n", - object_get_canonical_path(OBJECT(s)), - FIELD_EX32(bit, EFUSE_RD_ADDR, PAGE), - FIELD_EX32(bit, EFUSE_RD_ADDR, ROW)); - - ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, RD_ERROR, 1); - } - - ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, RD_DONE, 1); - efuse_imr_update_irq(s); - return; -} - -static uint64_t efuse_cache_load_prew(RegisterInfo *reg, uint64_t val64) -{ - EFUSE_CTRL *s = XILINX_EFUSE_CTRL(reg->opaque); - - if (val64 & R_EFUSE_CACHE_LOAD_LOAD_MASK) { - efuse_data_sync(s); - - ARRAY_FIELD_DP32(s->regs, STATUS, CACHE_DONE, 1); - efuse_imr_update_irq(s); - } - - return 0; -} - -static uint64_t efuse_pgm_lock_prew(RegisterInfo *reg, uint64_t val64) -{ - EFUSE_CTRL *s = XILINX_EFUSE_CTRL(reg->opaque); - - /* Ignore all other bits */ - val64 = FIELD_EX32(val64, EFUSE_PGM_LOCK, SPK_ID_LOCK); - - /* Once the bit is written 1, only reset will clear it to 0 */ - val64 |= ARRAY_FIELD_EX32(s->regs, EFUSE_PGM_LOCK, SPK_ID_LOCK); - - return val64; -} - -static void efuse_aes_crc_postw(RegisterInfo *reg, uint64_t val64) -{ - efuse_key_crc_check(reg, val64, - R_STATUS_AES_CRC_PASS_MASK, - R_STATUS_AES_CRC_DONE_MASK, - EFUSE_AES_KEY_START, - EFUSE_AES_KEY_CRC_LK_MASK); -} - -static void efuse_aes_u0_crc_postw(RegisterInfo *reg, uint64_t val64) -{ - efuse_key_crc_check(reg, val64, - R_STATUS_AES_USER_KEY_0_CRC_PASS_MASK, - R_STATUS_AES_USER_KEY_0_CRC_DONE_MASK, - EFUSE_USER_KEY_0_START, - EFUSE_USER_KEY_0_CRC_LK_MASK); -} - -static void efuse_aes_u1_crc_postw(RegisterInfo *reg, uint64_t val64) -{ - efuse_key_crc_check(reg, val64, - R_STATUS_AES_USER_KEY_1_CRC_PASS_MASK, - R_STATUS_AES_USER_KEY_1_CRC_DONE_MASK, - EFUSE_USER_KEY_1_START, - EFUSE_USER_KEY_1_CRC_LK_MASK); -} - -static uint64_t efuse_wr_lock_prew(RegisterInfo *reg, uint64_t val) -{ - return val != R_WR_LOCK_UNLOCK_PASSCODE; -} - -static const RegisterAccessInfo efuse_ctrl_regs_info[] = { - { .name = "WR_LOCK", .addr = A_WR_LOCK, - .reset = 0x1, - .pre_write = efuse_wr_lock_prew, - },{ .name = "CFG", .addr = A_CFG, - .rsvd = 0x9, - },{ .name = "STATUS", .addr = A_STATUS, - .rsvd = 0x8, - .ro = 0xfff, - },{ .name = "EFUSE_PGM_ADDR", .addr = A_EFUSE_PGM_ADDR, - .post_write = efuse_pgm_addr_postw, - },{ .name = "EFUSE_RD_ADDR", .addr = A_EFUSE_RD_ADDR, - .rsvd = 0x1f, - .post_write = efuse_rd_addr_postw, - },{ .name = "EFUSE_RD_DATA", .addr = A_EFUSE_RD_DATA, - .ro = 0xffffffff, - },{ .name = "TPGM", .addr = A_TPGM, - },{ .name = "TRD", .addr = A_TRD, - .reset = 0x19, - },{ .name = "TSU_H_PS", .addr = A_TSU_H_PS, - .reset = 0xff, - },{ .name = "TSU_H_PS_CS", .addr = A_TSU_H_PS_CS, - .reset = 0x11, - },{ .name = "TRDM", .addr = A_TRDM, - .reset = 0x3a, - },{ .name = "TSU_H_CS", .addr = A_TSU_H_CS, - .reset = 0x16, - },{ .name = "EFUSE_ISR", .addr = A_EFUSE_ISR, - .rsvd = 0x7fff8000, - .w1c = 0x80007fff, - .post_write = efuse_isr_postw, - },{ .name = "EFUSE_IMR", .addr = A_EFUSE_IMR, - .reset = 0x80007fff, - .rsvd = 0x7fff8000, - .ro = 0xffffffff, - },{ .name = "EFUSE_IER", .addr = A_EFUSE_IER, - .rsvd = 0x7fff8000, - .pre_write = efuse_ier_prew, - },{ .name = "EFUSE_IDR", .addr = A_EFUSE_IDR, - .rsvd = 0x7fff8000, - .pre_write = efuse_idr_prew, - },{ .name = "EFUSE_CACHE_LOAD", .addr = A_EFUSE_CACHE_LOAD, - .pre_write = efuse_cache_load_prew, - },{ .name = "EFUSE_PGM_LOCK", .addr = A_EFUSE_PGM_LOCK, - .pre_write = efuse_pgm_lock_prew, - },{ .name = "EFUSE_AES_CRC", .addr = A_EFUSE_AES_CRC, - .post_write = efuse_aes_crc_postw, - },{ .name = "EFUSE_AES_USR_KEY0_CRC", .addr = A_EFUSE_AES_USR_KEY0_CRC, - .post_write = efuse_aes_u0_crc_postw, - },{ .name = "EFUSE_AES_USR_KEY1_CRC", .addr = A_EFUSE_AES_USR_KEY1_CRC, - .post_write = efuse_aes_u1_crc_postw, - },{ .name = "EFUSE_PD", .addr = A_EFUSE_PD, - .ro = 0xfffffffe, - },{ .name = "EFUSE_ANLG_OSC_SW_1LP", .addr = A_EFUSE_ANLG_OSC_SW_1LP, - },{ .name = "EFUSE_TEST_CTRL", .addr = A_EFUSE_TEST_CTRL, - .reset = 0x8, - } -}; - -static MemTxResult efuse_ctrl_write_with_attrs(void *opaque, hwaddr addr, - uint64_t v64, unsigned size, - MemTxAttrs attrs) -{ - EFUSE_CTRL *s; - RegisterInfoArray *reg_array = opaque; - Object *dev; - - assert(reg_array != NULL); - - dev = reg_array->mem.owner; - assert(dev); - - s = XILINX_EFUSE_CTRL(dev); - - if (addr != A_WR_LOCK && s->regs[R_WR_LOCK]) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s[reg_0x%02lx]: Attempt to write locked register.\n", - object_get_canonical_path(OBJECT(s)), (long)addr); - return MEMTX_ERROR; - } - - return register_write_memory_with_attrs(opaque, addr, v64, size, attrs); -} - -static void efuse_ctrl_register_reset(RegisterInfo *reg) -{ - if (!reg->data || !reg->access) { - return; - } - - /* Reset must not trigger some registers' writers */ - switch (reg->access->addr) { - case A_EFUSE_PGM_ADDR: - case A_EFUSE_RD_ADDR: - case A_EFUSE_AES_CRC: - case A_EFUSE_AES_USR_KEY0_CRC: - case A_EFUSE_AES_USR_KEY1_CRC: - *(uint32_t *)reg->data = reg->access->reset; - return; - } - - register_reset(reg); -} - -static void efuse_ctrl_reset(DeviceState *dev) -{ - EFUSE_CTRL *s = XILINX_EFUSE_CTRL(dev); - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { - efuse_ctrl_register_reset(&s->regs_info[i]); - } - - efuse_anchor_bits_check(s); - efuse_data_sync(s); - efuse_imr_update_irq(s); -} - -static const MemoryRegionOps efuse_ctrl_ops = { - .read = register_read_memory, - .write_with_attrs = efuse_ctrl_write_with_attrs, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void efuse_ctrl_realize(DeviceState *dev, Error **errp) -{ - EFUSE_CTRL *s = XILINX_EFUSE_CTRL(dev); - const char *prefix = object_get_canonical_path(OBJECT(dev)); - - if (!s->efuse) { - error_setg(&error_abort, "%s: XLN-EFUSE not connected", prefix); - } - - if (!s->aes_key_sink) { - error_setg(&error_abort, - "%s: eFuse AES key sink not connected", prefix); - } - - if (!s->usr_key0_sink) { - error_setg(&error_abort, - "%s: eFuse USR_KEY0 key sink not connected", prefix); - } - - if (!s->usr_key1_sink) { - error_setg(&error_abort, - "%s: eFuse USR_KEY1 key sink not connected", prefix); - } - - /* Sort property-defined pgm-locks for bsearch lookup */ - efuse_lk_spec_sort(s); -} - -static void efuse_ctrl_init(Object *obj) -{ - EFUSE_CTRL *s = XILINX_EFUSE_CTRL(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - RegisterInfoArray *reg_array; - - memory_region_init(&s->iomem, obj, - TYPE_XILINX_EFUSE_CTRL, EFUSE_CTRL_R_MAX * 4); - reg_array = - register_init_block32(DEVICE(obj), efuse_ctrl_regs_info, - ARRAY_SIZE(efuse_ctrl_regs_info), - s->regs_info, s->regs, - &efuse_ctrl_ops, - XILINX_EFUSE_CTRL_ERR_DEBUG, - EFUSE_CTRL_R_MAX * 4); - memory_region_add_subregion(&s->iomem, - 0x0, - ®_array->mem); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq_efuse_imr); -} - -static const VMStateDescription vmstate_efuse_ctrl = { - .name = TYPE_XILINX_EFUSE_CTRL, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, EFUSE_CTRL, EFUSE_CTRL_R_MAX), - VMSTATE_END_OF_LIST(), - } -}; - -static Property efuse_ctrl_props[] = { - DEFINE_PROP_LINK("efuse", - EFUSE_CTRL, efuse, - TYPE_XLNX_EFUSE, XLNXEFuse *), - DEFINE_PROP_ARRAY("pg0-lock", - EFUSE_CTRL, extra_pg0_lock_n16, extra_pg0_lock.u16, - qdev_prop_uint16, uint16_t), - - DEFINE_PROP_LINK("zynqmp-aes-key-sink-efuses", - EFUSE_CTRL, aes_key_sink, - TYPE_ZYNQMP_AES_KEY_SINK, ZynqMPAESKeySink *), - DEFINE_PROP_LINK("zynqmp-aes-key-sink-efuses-user0", - EFUSE_CTRL, usr_key0_sink, - TYPE_ZYNQMP_AES_KEY_SINK, ZynqMPAESKeySink *), - DEFINE_PROP_LINK("zynqmp-aes-key-sink-efuses-user1", - EFUSE_CTRL, usr_key1_sink, - TYPE_ZYNQMP_AES_KEY_SINK, ZynqMPAESKeySink *), - - DEFINE_PROP_END_OF_LIST(), -}; - -static void efuse_ctrl_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = efuse_ctrl_reset; - dc->realize = efuse_ctrl_realize; - dc->vmsd = &vmstate_efuse_ctrl; - device_class_set_props(dc, efuse_ctrl_props); -} - -static const TypeInfo efuse_ctrl_info = { - .name = TYPE_XILINX_EFUSE_CTRL, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(EFUSE_CTRL), - .class_init = efuse_ctrl_class_init, - .instance_init = efuse_ctrl_init, -}; - -static void efuse_ctrl_register_types(void) -{ - type_register_static(&efuse_ctrl_info); -} - -type_init(efuse_ctrl_register_types) - -/* - * Retrieve a row, with unreadable bits returned as 0. - */ -uint32_t versal_efuse_read_row(XLNXEFuse *efuse, uint32_t bit, bool *denied) -{ - bool dummy; - - if (!denied) { - denied = &dummy; - } - - if (bit >= EFUSE_RD_BLOCKED_START && bit <= EFUSE_RD_BLOCKED_END) { - *denied = true; - return 0; - } - - *denied = false; - return efuse_get_row(efuse, bit); -} diff --git a/hw/block/xlnx-versal-pmc-efuse.h b/hw/block/xlnx-versal-pmc-efuse.h deleted file mode 100644 index 54721ba02ad..00000000000 --- a/hw/block/xlnx-versal-pmc-efuse.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2020 Xilinx Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef XLNX_VERSAL_PMC_EFUSE_H -#define XLNX_VERSAL_PMC_EFUSE_H - -#include "qemu/osdep.h" -#include "hw/block/xlnx-efuse.h" - -uint32_t versal_efuse_read_row(XLNXEFuse *s, uint32_t bit, bool *denied); - -#endif From 01c38b0d7260eb3d9e391171040cd1d3efeeb7d6 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Thu, 24 Jul 2025 12:25:15 +0200 Subject: [PATCH 03/22] hw/misc/xlnx-versal-pm[xc]-crp: add reset traces Add reset related traces to the CRP models of the PMC and PMX sub-systems. Those traces are: - xlnx_versal_pmc_crp_power_on_reset - xlnx_versal_pmc_crp_soft_reset They are triggered on RST_PS writes and report the subsystem (PS, PL or PMC) for which the reset state is toggled. Those traces can help developers to catch reset events on the sub-systems. Signed-off-by: Luc Michel Signed-off-by: Francisco Iglesias --- hw/misc/trace-events | 4 ++++ hw/misc/xlnx-versal-pmc-clk-rst.c | 35 +++++++++++++++++++++++++++++++ hw/misc/xlnx-versal-pmx-crp.c | 35 +++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index fe139ad473d..a8db5cfa2bc 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -341,6 +341,10 @@ xlnx_versal_ecdsa_rsa_exec_opcode(const char *insn) "%s" xlnx_versal_ecdsa_rsa_exec_ucode(unsigned int word, int pc, const char *op, int len, unsigned int r, unsigned int a, unsigned int b) "%02x:%02x: %s_%d 0x%x <- 0x%x, 0x%x" xlnx_versal_ecdsa_rsa_exec_stop(unsigned int word, int pc) " %02x:%02x: stop" +# xlnx-versal-pmc-clk-rst.c and xlnx-versal-pmx-crp.c +xlnx_versal_pmc_crp_power_on_reset(const char *subsys, bool val) "%s: %d" +xlnx_versal_pmc_crp_soft_reset(const char *subsys, bool val) "%s: %d" + # xilinx-psxc-rpu-cluster-core.c xilinx_psxc_rpu_cluster_core_read(uint64_t addr, uint64_t value, unsigned int size) "addr: 0x%" PRIx64 " value: 0x%" PRIx64 " size: %u" xilinx_psxc_rpu_cluster_core_write(uint64_t addr, uint64_t value, unsigned int size) "addr: 0x%" PRIx64 " value: 0x%" PRIx64 " size: %u" diff --git a/hw/misc/xlnx-versal-pmc-clk-rst.c b/hw/misc/xlnx-versal-pmc-clk-rst.c index b664dd13ee6..7e218e927a1 100644 --- a/hw/misc/xlnx-versal-pmc-clk-rst.c +++ b/hw/misc/xlnx-versal-pmc-clk-rst.c @@ -37,6 +37,7 @@ #include "hw/qdev-properties.h" #include "hw/fdt_generic_util.h" +#include "trace.h" #ifndef XILINX_CRP_ERR_DEBUG #define XILINX_CRP_ERR_DEBUG 0 @@ -601,6 +602,40 @@ static uint64_t rst_ps_prew(RegisterInfo *reg, uint64_t val64) { CRP *s = XILINX_CRP(reg->opaque); uint32_t val = val64; + uint32_t flip = val ^ s->regs[R_RST_PS]; + + if (FIELD_EX32(flip, RST_PS, PMC_SRST)) { + trace_xlnx_versal_pmc_crp_soft_reset("pmc", + FIELD_EX32(val, RST_PS, PMC_SRST)); + } + + if (FIELD_EX32(flip, RST_PS, PS_SRST)) { + trace_xlnx_versal_pmc_crp_soft_reset("ps", + FIELD_EX32(val, RST_PS, PS_SRST)); + } + + if (FIELD_EX32(flip, RST_PS, PL_SRST)) { + trace_xlnx_versal_pmc_crp_soft_reset("pl", + FIELD_EX32(val, RST_PS, PL_SRST)); + } + + if (FIELD_EX32(flip, RST_PS, PMC_POR)) { + trace_xlnx_versal_pmc_crp_power_on_reset("pmc", + FIELD_EX32(val, RST_PS, + PMC_POR)); + } + + if (FIELD_EX32(flip, RST_PS, PS_POR)) { + trace_xlnx_versal_pmc_crp_power_on_reset("ps", + FIELD_EX32(val, RST_PS, + PS_POR)); + } + + if (FIELD_EX32(flip, RST_PS, PL_POR)) { + trace_xlnx_versal_pmc_crp_power_on_reset("pl", + FIELD_EX32(val, RST_PS, + PL_POR)); + } ARRAY_FIELD_DP32(s->regs, RESET_REASON, SW_SYS, ARRAY_FIELD_EX32(s->regs, RESET_REASON, SW_SYS) | diff --git a/hw/misc/xlnx-versal-pmx-crp.c b/hw/misc/xlnx-versal-pmx-crp.c index 8ae71e8b9ef..8d1ba10b67e 100644 --- a/hw/misc/xlnx-versal-pmx-crp.c +++ b/hw/misc/xlnx-versal-pmx-crp.c @@ -34,6 +34,7 @@ #include "qemu/config-file.h" #include "qemu/option.h" #include "hw/fdt_generic_util.h" +#include "trace.h" #ifndef XILINX_PMX_CRP_ERR_DEBUG #define XILINX_PMX_CRP_ERR_DEBUG 0 @@ -577,6 +578,40 @@ static uint64_t rst_ps_prew(RegisterInfo *reg, uint64_t val64) { PMX_CRP *s = XILINX_PMX_CRP(reg->opaque); uint32_t val = val64; + uint32_t flip = val ^ s->regs[R_RST_PS]; + + if (FIELD_EX32(flip, RST_PS, PMC_SRST)) { + trace_xlnx_versal_pmc_crp_soft_reset("pmc", + FIELD_EX32(val, RST_PS, PMC_SRST)); + } + + if (FIELD_EX32(flip, RST_PS, PS_SRST)) { + trace_xlnx_versal_pmc_crp_soft_reset("ps", + FIELD_EX32(val, RST_PS, PS_SRST)); + } + + if (FIELD_EX32(flip, RST_PS, PL_SRST)) { + trace_xlnx_versal_pmc_crp_soft_reset("pl", + FIELD_EX32(val, RST_PS, PL_SRST)); + } + + if (FIELD_EX32(flip, RST_PS, PMC_POR)) { + trace_xlnx_versal_pmc_crp_power_on_reset("pmc", + FIELD_EX32(val, RST_PS, + PMC_POR)); + } + + if (FIELD_EX32(flip, RST_PS, PS_POR)) { + trace_xlnx_versal_pmc_crp_power_on_reset("ps", + FIELD_EX32(val, RST_PS, + PS_POR)); + } + + if (FIELD_EX32(flip, RST_PS, PL_POR)) { + trace_xlnx_versal_pmc_crp_power_on_reset("pl", + FIELD_EX32(val, RST_PS, + PL_POR)); + } ARRAY_FIELD_DP32(s->regs, RESET_REASON, SW_SYS, ARRAY_FIELD_EX32(s->regs, RESET_REASON, SW_SYS) | From cc174705a4030ea5b80fb1457a914e6fcb33855b Mon Sep 17 00:00:00 2001 From: Sai Pavan Boddu Date: Mon, 28 Jul 2025 11:39:52 +0530 Subject: [PATCH 04/22] xilinx-zynqmp-gicp: Remove reserved fields GIC proxy is a generic model used across different platforms. A few platforms have irqs connected in fields currently reserved. Signed-off-by: Sai Pavan Boddu Signed-off-by: Francisco Iglesias --- hw/intc/xilinx-zynqmp-gicp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/intc/xilinx-zynqmp-gicp.c b/hw/intc/xilinx-zynqmp-gicp.c index e422af2ba21..e704715bd4f 100644 --- a/hw/intc/xilinx-zynqmp-gicp.c +++ b/hw/intc/xilinx-zynqmp-gicp.c @@ -56,11 +56,11 @@ REG32(GICP0_IRQ_MASK, 0x4) REG32(GICP0_IRQ_ENABLE, 0x8) REG32(GICP0_IRQ_DISABLE, 0xc) REG32(GICP0_IRQ_TRIGGER, 0x10) - #define R_GICP0_RSVD 0x000000ff + #define R_GICP0_RSVD 0x00000000 #define R_GICP1_RSVD 0 #define R_GICP2_RSVD 0 - #define R_GICP3_RSVD 0x000000ff - #define R_GICP4_RSVD 0xf0000000 + #define R_GICP3_RSVD 0x00000000 + #define R_GICP4_RSVD 0x00000000 #define R_GICP5_RSVD 0x00000000 #define R_GICP6_RSVD 0x00000000 #define R_GICP7_RSVD 0x00000000 From 507007b2aacb7005abebc49abf049b1d319dd4ed Mon Sep 17 00:00:00 2001 From: John Vicky Vykuntapu Date: Tue, 29 Jul 2025 12:16:25 +0530 Subject: [PATCH 05/22] target/arm: Correct and include FEAT_LSE support on the Cortex-A78 model Set atomic field in ID_AA64ISAR0_EL1 to 0b0010 to indicate support for FEAT_LSE (Large System Extensions) atomic instructions. This enables guests to detect atomic support via the standard aarch64 feature register for Cortex-A78. Signed-off-by: John Vicky Vykuntapu Signed-off-by: Francisco Iglesias --- target/arm/tcg/cpu64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index d1dd35e41e0..87633474df5 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -349,7 +349,7 @@ static void aarch64_a78_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); t = FIELD_DP64(t, ID_AA64ISAR0, SHA2, 2); /* SHA512 */ t = FIELD_DP64(t, ID_AA64ISAR0, CRC32, 1); - t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 0); + t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 2); t = FIELD_DP64(t, ID_AA64ISAR0, RDM, 1); t = FIELD_DP64(t, ID_AA64ISAR0, SHA3, 1); t = FIELD_DP64(t, ID_AA64ISAR0, SM3, 1); From 6d62b399fe856164e61859463a0eaeacdfdeb874 Mon Sep 17 00:00:00 2001 From: Shiva sagar Myana Date: Thu, 13 Mar 2025 18:20:05 +0530 Subject: [PATCH 06/22] hw/misc/xilinx_zynqmp_ams: Set ps_sysmon ready status Set ps sysmon startup_state status to Idle (PS SysMon is Ready), this will fix the xilinx-ams probe failed error. Signed-off-by: Shiva sagar Myana Signed-off-by: Francisco Iglesias --- hw/misc/xilinx_zynqmp_ams.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/misc/xilinx_zynqmp_ams.c b/hw/misc/xilinx_zynqmp_ams.c index 7898fbf895f..18b8c27d3f3 100644 --- a/hw/misc/xilinx_zynqmp_ams.c +++ b/hw/misc/xilinx_zynqmp_ams.c @@ -503,6 +503,7 @@ static void ams_reset(DeviceState *dev) register_reset(&s->regs_info[i]); } + ARRAY_FIELD_DP32(s->regs, PS_SYSMON_CONTROL_STATUS, STARTUP_STATE, 8); imr_0_update_irq(s); imr_1_update_irq(s); } From 2bcfc4c97dfe8682202679bbe81b15088f0b5a15 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Thu, 28 Aug 2025 12:29:44 +0200 Subject: [PATCH 07/22] hw/dma/xlnx-zdma: turn log traces to guest errors The decode error traces were logged using qemu_log(). Use qemu_log_mask() with LOG_GUEST_ERROR instead to avoid polluting stderr. Signed-off-by: Luc Michel Signed-off-by: Francisco Iglesias --- hw/dma/xlnx-zdma.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c index 073c4ff1fa3..5f23e6887d8 100644 --- a/hw/dma/xlnx-zdma.c +++ b/hw/dma/xlnx-zdma.c @@ -744,9 +744,8 @@ static uint64_t zdma_read(void *opaque, hwaddr addr, unsigned size) if (!r->data) { char *path = object_get_canonical_path(OBJECT(s)); - qemu_log("%s: Decode error: read from %" HWADDR_PRIx "\n", - path, - addr); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Decode error: read from %" + HWADDR_PRIx "\n", path, addr); g_free(path); ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true); zdma_ch_imr_update_irq(s); @@ -763,9 +762,8 @@ static void zdma_write(void *opaque, hwaddr addr, uint64_t value, if (!r->data) { char *path = object_get_canonical_path(OBJECT(s)); - qemu_log("%s: Decode error: write to %" HWADDR_PRIx "=%" PRIx64 "\n", - path, - addr, value); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Decode error: write to %" + HWADDR_PRIx "=%" PRIx64 "\n", path, addr, value); g_free(path); ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true); zdma_ch_imr_update_irq(s); From 741b8084a53a5c640966dd5fc12ca334eb02f4cc Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Thu, 28 Aug 2025 12:29:45 +0200 Subject: [PATCH 08/22] hw/crypto/xlnx-asu-aes: send a non-zero data mask when CM are on With DCA counter-measures enabled, when the device send block, it alternates between a pseudo-random value block and the data block masked by this value. Until now the model was sending a block full of zeros as a mask. This can be an issue if the firmware checks the mask as the counter-measure become useless with a null mask. Change this to a constant non-null mask block, which should be enough for most use-cases. Signed-off-by: Luc Michel Signed-off-by: Francisco Iglesias --- hw/crypto/xlnx-asu-aes.c | 56 ++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/hw/crypto/xlnx-asu-aes.c b/hw/crypto/xlnx-asu-aes.c index 2b8054d6a20..ded45d882aa 100644 --- a/hw/crypto/xlnx-asu-aes.c +++ b/hw/crypto/xlnx-asu-aes.c @@ -850,10 +850,19 @@ static void sink_notify_cb(void *opaque) send_aes_payload(s); } +/* + * Some random bytes. The real device sends constantly changing pseudo-random + * mask blocks. Here the mask block is always the same, as DCA counter-measures + * are not really a concerns in a QEMU model. However we don't send a constant + * zero block as some firmware check that. + */ +static const AsuAesBlock SEND_DATA_MASK = { + 0xee, 0x9a, 0xc7, 0x69, 0xc0, 0x2e, 0xa9, 0x09, + 0x38, 0xd9, 0xa2, 0x2b, 0x57, 0x16, 0x8b, 0x93, +}; + static bool send_aes_data_mask(XilinxAsuAesState *s) { - uint8_t zeros[ASU_AES_BLOCK_SIZE]; - if (s->mask_sent) { return true; } @@ -863,17 +872,42 @@ static bool send_aes_data_mask(XilinxAsuAesState *s) return false; } - memset(zeros, 0, sizeof(zeros)); - trace_xilinx_asu_aes_send_mask(ASU_AES_BLOCK_SIZE, false); - stream_push(s->sink, zeros, sizeof(zeros), false); + stream_push(s->sink, (uint8_t *)SEND_DATA_MASK, + sizeof(SEND_DATA_MASK), false); s->mask_sent = true; return true; } +static void send_aes_data(XilinxAsuAesState *s) +{ + AsuAesBlock out; + + trace_xilinx_asu_aes_send(ASU_AES_BLOCK_SIZE, s->eop); + + if (!data_split_enabled(s) && !s->data_swap) { + /* no block copy required */ + stream_push(s->sink, s->aes_ctx.out, ASU_AES_BLOCK_SIZE, s->eop); + return; + } + + block_copy(out, s->aes_ctx.out); + + if (s->data_swap) { + block_bswap(out); + } + + if (data_split_enabled(s)) { + block_xor(out, out, SEND_DATA_MASK); + } + + stream_push(s->sink, out, ASU_AES_BLOCK_SIZE, s->eop); +} + static void send_aes_payload(XilinxAsuAesState *s) { + if (data_split_enabled(s) && !send_aes_data_mask(s)) { return; } @@ -883,17 +917,7 @@ static void send_aes_payload(XilinxAsuAesState *s) return; } - trace_xilinx_asu_aes_send(ASU_AES_BLOCK_SIZE, s->eop); - - if (s->data_swap) { - AsuAesBlock out; - - block_copy(out, s->aes_ctx.out); - block_bswap(out); - stream_push(s->sink, out, ASU_AES_BLOCK_SIZE, s->eop); - } else { - stream_push(s->sink, s->aes_ctx.out, ASU_AES_BLOCK_SIZE, s->eop); - } + send_aes_data(s); s->ready = true; From 9689c862ffa0693e3937fd2764241dd0d2675ca0 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Tue, 2 Sep 2025 15:27:03 +0200 Subject: [PATCH 09/22] hw/gpio/xilinx_iomod_gpi: add a comment about the GPI_ENABLE register Add a comment explaining what this GPI_ENABLE register is about for the next reader of the code. Signed-off-by: Luc Michel Signed-off-by: Francisco Iglesias --- hw/gpio/xilinx_iomod_gpi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/gpio/xilinx_iomod_gpi.c b/hw/gpio/xilinx_iomod_gpi.c index 774115ec999..11f8aba0bd6 100644 --- a/hw/gpio/xilinx_iomod_gpi.c +++ b/hw/gpio/xilinx_iomod_gpi.c @@ -120,6 +120,16 @@ static const RegisterAccessInfo gpi_regs_info[] = { { .name = "GPI", .addr = A_IOM_GPI, .ro = ~0 }, }; +/* + * This register does not belong to this device. It is a hack to easily + * implement the corresponding mask register found in the ZynqMP PMU_LOCAL + * device (GPIx_ENABLE registers). The memory region corresponding to this + * register is mapped on top of the PMU_LOCAL one at the correct offset, + * effectively shadowing the GPIx_ENABLE register. + * + * A proper fix would consist in rewiring the GPIOs so that they go through the + * PMU_LOCAL device. They could then be masked from there. + */ static const RegisterAccessInfo gpi_en_regs_info[] = { { .name = "GPI_ENABLE", .addr = 0, .post_write = gpi_en_postw }, }; From d383797ca4c44d05a6aca148eb342ad4555004b8 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Tue, 2 Sep 2025 15:27:04 +0200 Subject: [PATCH 10/22] hw/gpio/xilinx_iomod_gpi: do not expose masked GPIs Do not expose a GPI in the device register when it is masked (not enabled) in the PMU_LOCAL. Note that because of the GPI_ENABLE hack, this fix is done in this device model and not in the PMU_LOCAL one. Signed-off-by: Luc Michel Signed-off-by: Francisco Iglesias --- hw/gpio/xilinx_iomod_gpi.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/gpio/xilinx_iomod_gpi.c b/hw/gpio/xilinx_iomod_gpi.c index 11f8aba0bd6..566f83f2571 100644 --- a/hw/gpio/xilinx_iomod_gpi.c +++ b/hw/gpio/xilinx_iomod_gpi.c @@ -116,8 +116,15 @@ static void gpi_en_postw(RegisterInfo *reg, uint64_t val64) update_irq(s); } +static uint64_t gpi_postr(RegisterInfo *reg, uint64_t val64) +{ + XilinxGPI *s = XILINX_IO_MODULE_GPI(reg->opaque); + + return val64 & s->ien; +} + static const RegisterAccessInfo gpi_regs_info[] = { - { .name = "GPI", .addr = A_IOM_GPI, .ro = ~0 }, + { .name = "GPI", .addr = A_IOM_GPI, .ro = ~0, .post_read = gpi_postr }, }; /* From 3132c861f47933f8a0b39472c02bce60258218aa Mon Sep 17 00:00:00 2001 From: Shiva sagar Myana Date: Wed, 3 Sep 2025 19:46:30 +0530 Subject: [PATCH 11/22] hw/dma/xlnx-zdma: split into base/concrete classes Split the TYPE_XLNX_ZDMA type into base and concrete classes. This is in preparation for the Versal Gen 2 version of the zdma. Signed-off-by: Shiva sagar Myana Signed-off-by: Francisco Iglesias --- hw/dma/xlnx-zdma.c | 153 ++++++++++++++++++++++++------------- include/hw/dma/xlnx-zdma.h | 19 +++-- 2 files changed, 112 insertions(+), 60 deletions(-) diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c index 5f23e6887d8..aae84750ac7 100644 --- a/hw/dma/xlnx-zdma.c +++ b/hw/dma/xlnx-zdma.c @@ -218,7 +218,7 @@ enum { AXI_BURST_INCR = 1, }; -static inline uint32_t zdma_non_parity_mask(XlnxZDMA *s, uint32_t val) +static inline uint32_t zdma_non_parity_mask(XlnxZDMABase *s, uint32_t val) { if (!s->cfg.has_parity) { val &= 0xfff; @@ -226,7 +226,7 @@ static inline uint32_t zdma_non_parity_mask(XlnxZDMA *s, uint32_t val) return val; } -static void zdma_ch_imr_update_irq(XlnxZDMA *s) +static void zdma_ch_imr_update_irq(XlnxZDMABase *s) { bool pending; @@ -237,13 +237,13 @@ static void zdma_ch_imr_update_irq(XlnxZDMA *s) static void zdma_ch_isr_postw(RegisterInfo *reg, uint64_t val64) { - XlnxZDMA *s = XLNX_ZDMA(reg->opaque); + XlnxZDMABase *s = XLNX_ZDMA_BASE(reg->opaque); zdma_ch_imr_update_irq(s); } static uint64_t zdma_ch_ien_prew(RegisterInfo *reg, uint64_t val64) { - XlnxZDMA *s = XLNX_ZDMA(reg->opaque); + XlnxZDMABase *s = XLNX_ZDMA_BASE(reg->opaque); uint32_t val = val64; s->regs[R_ZDMA_CH_IMR] &= ~val; @@ -253,7 +253,7 @@ static uint64_t zdma_ch_ien_prew(RegisterInfo *reg, uint64_t val64) static uint64_t zdma_ch_ids_prew(RegisterInfo *reg, uint64_t val64) { - XlnxZDMA *s = XLNX_ZDMA(reg->opaque); + XlnxZDMABase *s = XLNX_ZDMA_BASE(reg->opaque); uint32_t val = val64; val = zdma_non_parity_mask(s, val); @@ -263,7 +263,7 @@ static uint64_t zdma_ch_ids_prew(RegisterInfo *reg, uint64_t val64) return 0; } -static void zdma_set_state(XlnxZDMA *s, XlnxZDMAState state) +static void zdma_set_state(XlnxZDMABase *s, XlnxZDMAState state) { s->state = state; ARRAY_FIELD_DP32(s->regs, ZDMA_CH_STATUS, STATE, state); @@ -274,7 +274,7 @@ static void zdma_set_state(XlnxZDMA *s, XlnxZDMAState state) } } -static void zdma_src_done(XlnxZDMA *s) +static void zdma_src_done(XlnxZDMABase *s) { unsigned int cnt; cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT); @@ -289,7 +289,7 @@ static void zdma_src_done(XlnxZDMA *s) zdma_ch_imr_update_irq(s); } -static void zdma_dst_done(XlnxZDMA *s) +static void zdma_dst_done(XlnxZDMABase *s) { unsigned int cnt; cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT); @@ -304,7 +304,7 @@ static void zdma_dst_done(XlnxZDMA *s) zdma_ch_imr_update_irq(s); } -static uint64_t zdma_get_regaddr64(XlnxZDMA *s, unsigned int basereg) +static uint64_t zdma_get_regaddr64(XlnxZDMABase *s, unsigned int basereg) { uint64_t addr; @@ -315,13 +315,14 @@ static uint64_t zdma_get_regaddr64(XlnxZDMA *s, unsigned int basereg) return addr; } -static void zdma_put_regaddr64(XlnxZDMA *s, unsigned int basereg, uint64_t addr) +static void zdma_put_regaddr64(XlnxZDMABase *s, unsigned int basereg, + uint64_t addr) { s->regs[basereg] = addr; s->regs[basereg + 1] = addr >> 32; } -static void zdma_load_descriptor_reg(XlnxZDMA *s, unsigned int reg, +static void zdma_load_descriptor_reg(XlnxZDMABase *s, unsigned int reg, XlnxZDMADescr *descr) { descr->addr = zdma_get_regaddr64(s, reg); @@ -329,7 +330,7 @@ static void zdma_load_descriptor_reg(XlnxZDMA *s, unsigned int reg, descr->attr = s->regs[reg + 3]; } -static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr, +static bool zdma_load_descriptor(XlnxZDMABase *s, uint64_t addr, XlnxZDMADescr *descr) { /* ZDMA descriptors must be aligned to their own size. */ @@ -348,7 +349,7 @@ static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr, return true; } -static void zdma_load_src_descriptor(XlnxZDMA *s) +static void zdma_load_src_descriptor(XlnxZDMABase *s) { uint64_t src_addr; unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE); @@ -365,7 +366,7 @@ static void zdma_load_src_descriptor(XlnxZDMA *s) } } -static void zdma_update_descr_addr(XlnxZDMA *s, bool type, +static void zdma_update_descr_addr(XlnxZDMABase *s, bool type, unsigned int basereg) { uint64_t addr, next; @@ -382,7 +383,7 @@ static void zdma_update_descr_addr(XlnxZDMA *s, bool type, zdma_put_regaddr64(s, basereg, next); } -static void zdma_load_dst_descriptor(XlnxZDMA *s) +static void zdma_load_dst_descriptor(XlnxZDMABase *s) { uint64_t dst_addr; unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE); @@ -404,7 +405,7 @@ static void zdma_load_dst_descriptor(XlnxZDMA *s) zdma_update_descr_addr(s, dst_type, R_ZDMA_CH_DST_CUR_DSCR_LSB); } -static void zdma_write_dst(XlnxZDMA *s, uint8_t *buf, uint32_t len) +static void zdma_write_dst(XlnxZDMABase *s, uint8_t *buf, uint32_t len) { uint32_t dst_size, dlen; bool dst_intr; @@ -463,7 +464,7 @@ static void zdma_write_dst(XlnxZDMA *s, uint8_t *buf, uint32_t len) } } -static void zdma_process_descr(XlnxZDMA *s) +static void zdma_process_descr(XlnxZDMABase *s) { uint64_t src_addr; uint32_t src_size, len; @@ -555,7 +556,7 @@ static void zdma_process_descr(XlnxZDMA *s) zdma_update_descr_addr(s, src_type, R_ZDMA_CH_SRC_CUR_DSCR_LSB); } -static void zdma_run(XlnxZDMA *s) +static void zdma_run(XlnxZDMABase *s) { while (s->state == ENABLED && !s->error) { zdma_load_src_descriptor(s); @@ -570,7 +571,7 @@ static void zdma_run(XlnxZDMA *s) zdma_ch_imr_update_irq(s); } -static void zdma_update_descr_addr_from_start(XlnxZDMA *s) +static void zdma_update_descr_addr_from_start(XlnxZDMABase *s) { uint64_t src_addr, dst_addr; @@ -583,7 +584,7 @@ static void zdma_update_descr_addr_from_start(XlnxZDMA *s) static void zdma_ch_ctrlx_postw(RegisterInfo *reg, uint64_t val64) { - XlnxZDMA *s = XLNX_ZDMA(reg->opaque); + XlnxZDMABase *s = XLNX_ZDMA_BASE(reg->opaque); if (ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL2, EN)) { s->error = false; @@ -725,11 +726,12 @@ static RegisterAccessInfo zdma_regs_info[] = { static void zdma_reset(DeviceState *dev) { - XlnxZDMA *s = XLNX_ZDMA(dev); + XlnxZDMA *sv1 = XLNX_ZDMA(dev); + XlnxZDMABase *s = XLNX_ZDMA_BASE(dev); unsigned int i; - for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { - register_reset(&s->regs_info[i]); + for (i = 0; i < ARRAY_SIZE(sv1->regs_info); ++i) { + register_reset(&sv1->regs_info[i]); } s->regs[R_ZDMA_CH_IMR] = zdma_non_parity_mask(s, s->regs[R_ZDMA_CH_IMR]); @@ -739,10 +741,10 @@ static void zdma_reset(DeviceState *dev) static uint64_t zdma_read(void *opaque, hwaddr addr, unsigned size) { - XlnxZDMA *s = XLNX_ZDMA(opaque); - RegisterInfo *r = &s->regs_info[addr / 4]; + XlnxZDMABase *s = XLNX_ZDMA_BASE(opaque); + RegisterInfo *r = s->reg_array->r[addr / 4]; - if (!r->data) { + if (!r) { char *path = object_get_canonical_path(OBJECT(s)); qemu_log_mask(LOG_GUEST_ERROR, "%s: Decode error: read from %" HWADDR_PRIx "\n", path, addr); @@ -757,10 +759,10 @@ static uint64_t zdma_read(void *opaque, hwaddr addr, unsigned size) static void zdma_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - XlnxZDMA *s = XLNX_ZDMA(opaque); - RegisterInfo *r = &s->regs_info[addr / 4]; + XlnxZDMABase *s = XLNX_ZDMA_BASE(opaque); + RegisterInfo *r = s->reg_array->r[addr / 4]; - if (!r->data) { + if (!r) { char *path = object_get_canonical_path(OBJECT(s)); qemu_log_mask(LOG_GUEST_ERROR, "%s: Decode error: write to %" HWADDR_PRIx "=%" PRIx64 "\n", path, addr, value); @@ -784,51 +786,84 @@ static const MemoryRegionOps zdma_ops = { static void zdma_set_sec(void *opaque, int n, int level) { - XlnxZDMA *s = XLNX_ZDMA(opaque); + XlnxZDMABase *s = XLNX_ZDMA_BASE(opaque); s->attr.secure = level; } -static void zdma_realize(DeviceState *dev, Error **errp) +static void zdma_populate_regs(DeviceState *owner, RegisterInfo *reg_info, + uint32_t *reg_data, RegisterInfoArray *reg_array, + RegisterAccessInfo *rae, size_t num_regs, + uint32_t regs_offset) { - XlnxZDMA *s = XLNX_ZDMA(dev); unsigned int i; - if (!s->dma_mr) { - error_setg(errp, TYPE_XLNX_ZDMA " 'dma' link not set"); - return; - } - address_space_init(&s->dma_as, s->dma_mr, "zdma-dma"); + for (i = 0; i < num_regs ; i++) { + int index = (regs_offset + rae[i].addr) / 4; + RegisterInfo *r = ®_info[index]; - for (i = 0; i < ARRAY_SIZE(zdma_regs_info); ++i) { - RegisterInfo *r = &s->regs_info[zdma_regs_info[i].addr / 4]; + object_initialize((void *)r, sizeof(*r), TYPE_REGISTER); *r = (RegisterInfo) { - .data = (uint8_t *)&s->regs[ - zdma_regs_info[i].addr / 4], + .data = (uint8_t *)®_data[index], .data_size = sizeof(uint32_t), - .access = &zdma_regs_info[i], - .opaque = s, + .access = &rae[i], + .opaque = owner, }; + reg_array->r[index] = r; } +} + +static void zdma_common_realize(DeviceState *dev, Error **errp) +{ + XlnxZDMABase *s = XLNX_ZDMA_BASE(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + if (!s->dma_mr) { + error_setg(errp, TYPE_XLNX_ZDMA " 'dma' link not set"); + return; + } + address_space_init(&s->dma_as, s->dma_mr, "zdma-dma"); s->attr = MEMTXATTRS_UNSPECIFIED; if (s->attr_ptr) { s->attr = *s->attr_ptr; } + sysbus_init_mmio(sbd, &s->reg_array->mem); + sysbus_init_irq(sbd, &s->irq_zdma_ch_imr); + qdev_init_gpio_in_named(dev, zdma_set_sec, "memattr-secure", 1); } +static void zdma_realize(DeviceState *dev, Error **errp) +{ + XlnxZDMA *sv1 = XLNX_ZDMA(dev); + XlnxZDMABase *s = XLNX_ZDMA_BASE(dev); + + zdma_common_realize(dev, errp); + if (*errp) { + return; + } + + s->regs = sv1->regs; + + zdma_populate_regs(dev, sv1->regs_info, sv1->regs, s->reg_array, + zdma_regs_info, ARRAY_SIZE(zdma_regs_info), 0x0); +} + static void zdma_init(Object *obj) { - XlnxZDMA *s = XLNX_ZDMA(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + XlnxZDMABase *s = XLNX_ZDMA_BASE(obj); + XlnxZDMA *sv1 = XLNX_ZDMA(obj); + + s->reg_array = g_new0(RegisterInfoArray, 1); + s->reg_array->r = g_new0(RegisterInfo *, ZDMA_R_MAX); + s->reg_array->num_elements = ZDMA_R_MAX; - memory_region_init_io(&s->iomem, obj, &zdma_ops, s, + memory_region_init_io(&s->reg_array->mem, OBJECT(sv1), &zdma_ops, sv1, TYPE_XLNX_ZDMA, ZDMA_R_MAX * 4); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq_zdma_ch_imr); + object_property_add_link(obj, "memattr", TYPE_MEMORY_TRANSACTION_ATTR, (Object **)&s->attr_ptr, qdev_prop_allow_set_link_before_realize, @@ -841,18 +876,18 @@ static const VMStateDescription vmstate_zdma = { .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, XlnxZDMA, ZDMA_R_MAX), - VMSTATE_UINT32(state, XlnxZDMA), - VMSTATE_UINT32_ARRAY(dsc_src.words, XlnxZDMA, 4), - VMSTATE_UINT32_ARRAY(dsc_dst.words, XlnxZDMA, 4), + VMSTATE_UINT32(parent_obj.state, XlnxZDMA), + VMSTATE_UINT32_ARRAY(parent_obj.dsc_src.words, XlnxZDMA, 4), + VMSTATE_UINT32_ARRAY(parent_obj.dsc_dst.words, XlnxZDMA, 4), VMSTATE_END_OF_LIST(), } }; static Property zdma_props[] = { - DEFINE_PROP_UINT32("bus-width", XlnxZDMA, cfg.bus_width, 64), - DEFINE_PROP_LINK("dma", XlnxZDMA, dma_mr, + DEFINE_PROP_UINT32("bus-width", XlnxZDMABase, cfg.bus_width, 64), + DEFINE_PROP_LINK("dma", XlnxZDMABase, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_BOOL("has-parity", XlnxZDMA, cfg.has_parity, 0), + DEFINE_PROP_BOOL("has-parity", XlnxZDMABase, cfg.has_parity, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -866,9 +901,16 @@ static void zdma_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_zdma; } +static const TypeInfo zdma_base_info = { + .name = TYPE_XLNX_ZDMA_BASE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxZDMABase), + .abstract = true, +}; + static const TypeInfo zdma_info = { .name = TYPE_XLNX_ZDMA, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_XLNX_ZDMA_BASE, .instance_size = sizeof(XlnxZDMA), .class_init = zdma_class_init, .instance_init = zdma_init, @@ -876,6 +918,7 @@ static const TypeInfo zdma_info = { static void zdma_register_types(void) { + type_register_static(&zdma_base_info); type_register_static(&zdma_info); } diff --git a/include/hw/dma/xlnx-zdma.h b/include/hw/dma/xlnx-zdma.h index de1fcf523c6..d4552ea0101 100644 --- a/include/hw/dma/xlnx-zdma.h +++ b/include/hw/dma/xlnx-zdma.h @@ -51,14 +51,14 @@ typedef union { uint32_t words[4]; } XlnxZDMADescr; -struct XlnxZDMA { +struct XlnxZDMABase { SysBusDevice parent_obj; - MemoryRegion iomem; MemTxAttrs *attr_ptr; MemTxAttrs attr; MemoryRegion *dma_mr; AddressSpace dma_as; qemu_irq irq_zdma_ch_imr; + RegisterInfoArray *reg_array; struct { uint32_t bus_width; @@ -71,16 +71,25 @@ struct XlnxZDMA { XlnxZDMADescr dsc_src; XlnxZDMADescr dsc_dst; - uint32_t regs[ZDMA_R_MAX]; - RegisterInfo regs_info[ZDMA_R_MAX]; - /* We don't model the common bufs. Must be at least 16 bytes to model write only mode. */ uint8_t buf[2048]; + + uint32_t *regs; +}; + +struct XlnxZDMA { + struct XlnxZDMABase parent_obj; + + uint32_t regs[ZDMA_R_MAX]; + RegisterInfo regs_info[ZDMA_R_MAX]; + }; #define TYPE_XLNX_ZDMA "xlnx.zdma" +#define TYPE_XLNX_ZDMA_BASE "xlnx-zdma-base" OBJECT_DECLARE_SIMPLE_TYPE(XlnxZDMA, XLNX_ZDMA) +OBJECT_DECLARE_SIMPLE_TYPE(XlnxZDMABase, XLNX_ZDMA_BASE) #endif /* XLNX_ZDMA_H */ From f8716885f81f534f85f528ade554c2090df52bef Mon Sep 17 00:00:00 2001 From: Shiva sagar Myana Date: Wed, 3 Sep 2025 19:46:31 +0530 Subject: [PATCH 12/22] hw/dma/xlnx-zdma: Restructure the interrupt registers for reusability This is a non functional restructure of the xlnx-zdma model to be able to reuse the interrupt registers in an updated version of the model found on AMD's Versal Gen 2. Signed-off-by: Shiva sagar Myana Signed-off-by: Francisco Iglesias --- hw/dma/xlnx-zdma.c | 199 ++++++++++++++++++++----------------- include/hw/dma/xlnx-zdma.h | 3 + 2 files changed, 109 insertions(+), 93 deletions(-) diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c index aae84750ac7..b4de965a9c8 100644 --- a/hw/dma/xlnx-zdma.c +++ b/hw/dma/xlnx-zdma.c @@ -42,70 +42,6 @@ REG32(ZDMA_ERR_CTRL, 0x0) FIELD(ZDMA_ERR_CTRL, APB_ERR_RES, 0, 1) -REG32(ZDMA_CH_ISR, 0x100) - FIELD(ZDMA_CH_ISR, LINK_LIST_PERR, 14, 1) - FIELD(ZDMA_CH_ISR, FREE_LIST_PERR, 13, 1) - FIELD(ZDMA_CH_ISR, WRBUF_PERR, 12, 1) - FIELD(ZDMA_CH_ISR, DMA_PAUSE, 11, 1) - FIELD(ZDMA_CH_ISR, DMA_DONE, 10, 1) - FIELD(ZDMA_CH_ISR, AXI_WR_DATA, 9, 1) - FIELD(ZDMA_CH_ISR, AXI_RD_DATA, 8, 1) - FIELD(ZDMA_CH_ISR, AXI_RD_DST_DSCR, 7, 1) - FIELD(ZDMA_CH_ISR, AXI_RD_SRC_DSCR, 6, 1) - FIELD(ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, 5, 1) - FIELD(ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, 4, 1) - FIELD(ZDMA_CH_ISR, BYTE_CNT_OVRFL, 3, 1) - FIELD(ZDMA_CH_ISR, DST_DSCR_DONE, 2, 1) - FIELD(ZDMA_CH_ISR, SRC_DSCR_DONE, 1, 1) - FIELD(ZDMA_CH_ISR, INV_APB, 0, 1) -REG32(ZDMA_CH_IMR, 0x104) - FIELD(ZDMA_CH_IMR, LINK_LIST_PERR, 14, 1) - FIELD(ZDMA_CH_IMR, FREE_LIST_PERR, 13, 1) - FIELD(ZDMA_CH_IMR, WRBUF_PERR, 12, 1) - FIELD(ZDMA_CH_IMR, DMA_PAUSE, 11, 1) - FIELD(ZDMA_CH_IMR, DMA_DONE, 10, 1) - FIELD(ZDMA_CH_IMR, AXI_WR_DATA, 9, 1) - FIELD(ZDMA_CH_IMR, AXI_RD_DATA, 8, 1) - FIELD(ZDMA_CH_IMR, AXI_RD_DST_DSCR, 7, 1) - FIELD(ZDMA_CH_IMR, AXI_RD_SRC_DSCR, 6, 1) - FIELD(ZDMA_CH_IMR, IRQ_DST_ACCT_ERR, 5, 1) - FIELD(ZDMA_CH_IMR, IRQ_SRC_ACCT_ERR, 4, 1) - FIELD(ZDMA_CH_IMR, BYTE_CNT_OVRFL, 3, 1) - FIELD(ZDMA_CH_IMR, DST_DSCR_DONE, 2, 1) - FIELD(ZDMA_CH_IMR, SRC_DSCR_DONE, 1, 1) - FIELD(ZDMA_CH_IMR, INV_APB, 0, 1) -REG32(ZDMA_CH_IEN, 0x108) - FIELD(ZDMA_CH_IEN, LINK_LIST_PERR, 14, 1) - FIELD(ZDMA_CH_IEN, FREE_LIST_PERR, 13, 1) - FIELD(ZDMA_CH_IEN, WRBUF_PERR, 12, 1) - FIELD(ZDMA_CH_IEN, DMA_PAUSE, 11, 1) - FIELD(ZDMA_CH_IEN, DMA_DONE, 10, 1) - FIELD(ZDMA_CH_IEN, AXI_WR_DATA, 9, 1) - FIELD(ZDMA_CH_IEN, AXI_RD_DATA, 8, 1) - FIELD(ZDMA_CH_IEN, AXI_RD_DST_DSCR, 7, 1) - FIELD(ZDMA_CH_IEN, AXI_RD_SRC_DSCR, 6, 1) - FIELD(ZDMA_CH_IEN, IRQ_DST_ACCT_ERR, 5, 1) - FIELD(ZDMA_CH_IEN, IRQ_SRC_ACCT_ERR, 4, 1) - FIELD(ZDMA_CH_IEN, BYTE_CNT_OVRFL, 3, 1) - FIELD(ZDMA_CH_IEN, DST_DSCR_DONE, 2, 1) - FIELD(ZDMA_CH_IEN, SRC_DSCR_DONE, 1, 1) - FIELD(ZDMA_CH_IEN, INV_APB, 0, 1) -REG32(ZDMA_CH_IDS, 0x10c) - FIELD(ZDMA_CH_IDS, LINK_LIST_PERR, 14, 1) - FIELD(ZDMA_CH_IDS, FREE_LIST_PERR, 13, 1) - FIELD(ZDMA_CH_IDS, WRBUF_PERR, 12, 1) - FIELD(ZDMA_CH_IDS, DMA_PAUSE, 11, 1) - FIELD(ZDMA_CH_IDS, DMA_DONE, 10, 1) - FIELD(ZDMA_CH_IDS, AXI_WR_DATA, 9, 1) - FIELD(ZDMA_CH_IDS, AXI_RD_DATA, 8, 1) - FIELD(ZDMA_CH_IDS, AXI_RD_DST_DSCR, 7, 1) - FIELD(ZDMA_CH_IDS, AXI_RD_SRC_DSCR, 6, 1) - FIELD(ZDMA_CH_IDS, IRQ_DST_ACCT_ERR, 5, 1) - FIELD(ZDMA_CH_IDS, IRQ_SRC_ACCT_ERR, 4, 1) - FIELD(ZDMA_CH_IDS, BYTE_CNT_OVRFL, 3, 1) - FIELD(ZDMA_CH_IDS, DST_DSCR_DONE, 2, 1) - FIELD(ZDMA_CH_IDS, SRC_DSCR_DONE, 1, 1) - FIELD(ZDMA_CH_IDS, INV_APB, 0, 1) REG32(ZDMA_CH_CTRL0, 0x110) FIELD(ZDMA_CH_CTRL0, OVR_FETCH, 7, 1) FIELD(ZDMA_CH_CTRL0, POINT_TYPE, 6, 1) @@ -192,6 +128,73 @@ REG32(ZDMA_CH_DBG1, 0x19c) REG32(ZDMA_CH_CTRL2, 0x200) FIELD(ZDMA_CH_CTRL2, EN, 0, 1) +/* Interrupt registers */ +REG32(ZDMA_CH_ISR, 0x00) + FIELD(ZDMA_CH_ISR, LINK_LIST_PERR, 14, 1) + FIELD(ZDMA_CH_ISR, FREE_LIST_PERR, 13, 1) + FIELD(ZDMA_CH_ISR, WRBUF_PERR, 12, 1) + FIELD(ZDMA_CH_ISR, DMA_PAUSE, 11, 1) + FIELD(ZDMA_CH_ISR, DMA_DONE, 10, 1) + FIELD(ZDMA_CH_ISR, AXI_WR_DATA, 9, 1) + FIELD(ZDMA_CH_ISR, AXI_RD_DATA, 8, 1) + FIELD(ZDMA_CH_ISR, AXI_RD_DST_DSCR, 7, 1) + FIELD(ZDMA_CH_ISR, AXI_RD_SRC_DSCR, 6, 1) + FIELD(ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, 5, 1) + FIELD(ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, 4, 1) + FIELD(ZDMA_CH_ISR, BYTE_CNT_OVRFL, 3, 1) + FIELD(ZDMA_CH_ISR, DST_DSCR_DONE, 2, 1) + FIELD(ZDMA_CH_ISR, SRC_DSCR_DONE, 1, 1) + FIELD(ZDMA_CH_ISR, INV_APB, 0, 1) +REG32(ZDMA_CH_IMR, 0x04) + FIELD(ZDMA_CH_IMR, LINK_LIST_PERR, 14, 1) + FIELD(ZDMA_CH_IMR, FREE_LIST_PERR, 13, 1) + FIELD(ZDMA_CH_IMR, WRBUF_PERR, 12, 1) + FIELD(ZDMA_CH_IMR, DMA_PAUSE, 11, 1) + FIELD(ZDMA_CH_IMR, DMA_DONE, 10, 1) + FIELD(ZDMA_CH_IMR, AXI_WR_DATA, 9, 1) + FIELD(ZDMA_CH_IMR, AXI_RD_DATA, 8, 1) + FIELD(ZDMA_CH_IMR, AXI_RD_DST_DSCR, 7, 1) + FIELD(ZDMA_CH_IMR, AXI_RD_SRC_DSCR, 6, 1) + FIELD(ZDMA_CH_IMR, IRQ_DST_ACCT_ERR, 5, 1) + FIELD(ZDMA_CH_IMR, IRQ_SRC_ACCT_ERR, 4, 1) + FIELD(ZDMA_CH_IMR, BYTE_CNT_OVRFL, 3, 1) + FIELD(ZDMA_CH_IMR, DST_DSCR_DONE, 2, 1) + FIELD(ZDMA_CH_IMR, SRC_DSCR_DONE, 1, 1) + FIELD(ZDMA_CH_IMR, INV_APB, 0, 1) +REG32(ZDMA_CH_IEN, 0x08) + FIELD(ZDMA_CH_IEN, LINK_LIST_PERR, 14, 1) + FIELD(ZDMA_CH_IEN, FREE_LIST_PERR, 13, 1) + FIELD(ZDMA_CH_IEN, WRBUF_PERR, 12, 1) + FIELD(ZDMA_CH_IEN, DMA_PAUSE, 11, 1) + FIELD(ZDMA_CH_IEN, DMA_DONE, 10, 1) + FIELD(ZDMA_CH_IEN, AXI_WR_DATA, 9, 1) + FIELD(ZDMA_CH_IEN, AXI_RD_DATA, 8, 1) + FIELD(ZDMA_CH_IEN, AXI_RD_DST_DSCR, 7, 1) + FIELD(ZDMA_CH_IEN, AXI_RD_SRC_DSCR, 6, 1) + FIELD(ZDMA_CH_IEN, IRQ_DST_ACCT_ERR, 5, 1) + FIELD(ZDMA_CH_IEN, IRQ_SRC_ACCT_ERR, 4, 1) + FIELD(ZDMA_CH_IEN, BYTE_CNT_OVRFL, 3, 1) + FIELD(ZDMA_CH_IEN, DST_DSCR_DONE, 2, 1) + FIELD(ZDMA_CH_IEN, SRC_DSCR_DONE, 1, 1) + FIELD(ZDMA_CH_IEN, INV_APB, 0, 1) +REG32(ZDMA_CH_IDS, 0x0c) + FIELD(ZDMA_CH_IDS, LINK_LIST_PERR, 14, 1) + FIELD(ZDMA_CH_IDS, FREE_LIST_PERR, 13, 1) + FIELD(ZDMA_CH_IDS, WRBUF_PERR, 12, 1) + FIELD(ZDMA_CH_IDS, DMA_PAUSE, 11, 1) + FIELD(ZDMA_CH_IDS, DMA_DONE, 10, 1) + FIELD(ZDMA_CH_IDS, AXI_WR_DATA, 9, 1) + FIELD(ZDMA_CH_IDS, AXI_RD_DATA, 8, 1) + FIELD(ZDMA_CH_IDS, AXI_RD_DST_DSCR, 7, 1) + FIELD(ZDMA_CH_IDS, AXI_RD_SRC_DSCR, 6, 1) + FIELD(ZDMA_CH_IDS, IRQ_DST_ACCT_ERR, 5, 1) + FIELD(ZDMA_CH_IDS, IRQ_SRC_ACCT_ERR, 4, 1) + FIELD(ZDMA_CH_IDS, BYTE_CNT_OVRFL, 3, 1) + FIELD(ZDMA_CH_IDS, DST_DSCR_DONE, 2, 1) + FIELD(ZDMA_CH_IDS, SRC_DSCR_DONE, 1, 1) + FIELD(ZDMA_CH_IDS, INV_APB, 0, 1) +#define ZDMA_R_INTR (R_ZDMA_CH_IDS + 1) + enum { PT_REG = 0, PT_MEM = 1, @@ -230,7 +233,7 @@ static void zdma_ch_imr_update_irq(XlnxZDMABase *s) { bool pending; - pending = s->regs[R_ZDMA_CH_ISR] & ~s->regs[R_ZDMA_CH_IMR]; + pending = s->regs_intr[R_ZDMA_CH_ISR] & ~s->regs_intr[R_ZDMA_CH_IMR]; qemu_set_irq(s->irq_zdma_ch_imr, pending); } @@ -246,7 +249,7 @@ static uint64_t zdma_ch_ien_prew(RegisterInfo *reg, uint64_t val64) XlnxZDMABase *s = XLNX_ZDMA_BASE(reg->opaque); uint32_t val = val64; - s->regs[R_ZDMA_CH_IMR] &= ~val; + s->regs_intr[R_ZDMA_CH_IMR] &= ~val; zdma_ch_imr_update_irq(s); return 0; } @@ -258,7 +261,7 @@ static uint64_t zdma_ch_ids_prew(RegisterInfo *reg, uint64_t val64) val = zdma_non_parity_mask(s, val); - s->regs[R_ZDMA_CH_IMR] |= val; + s->regs_intr[R_ZDMA_CH_IMR] |= val; zdma_ch_imr_update_irq(s); return 0; } @@ -280,11 +283,11 @@ static void zdma_src_done(XlnxZDMABase *s) cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT); cnt++; ARRAY_FIELD_DP32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT, cnt); - ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, SRC_DSCR_DONE, true); + ARRAY_FIELD_DP32(s->regs_intr, ZDMA_CH_ISR, SRC_DSCR_DONE, true); /* Did we overflow? */ if (cnt != ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT)) { - ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, true); + ARRAY_FIELD_DP32(s->regs_intr, ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, true); } zdma_ch_imr_update_irq(s); } @@ -295,11 +298,11 @@ static void zdma_dst_done(XlnxZDMABase *s) cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT); cnt++; ARRAY_FIELD_DP32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT, cnt); - ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DST_DSCR_DONE, true); + ARRAY_FIELD_DP32(s->regs_intr, ZDMA_CH_ISR, DST_DSCR_DONE, true); /* Did we overflow? */ if (cnt != ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT)) { - ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, true); + ARRAY_FIELD_DP32(s->regs_intr, ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, true); } zdma_ch_imr_update_irq(s); } @@ -362,7 +365,7 @@ static void zdma_load_src_descriptor(XlnxZDMABase *s) src_addr = zdma_get_regaddr64(s, R_ZDMA_CH_SRC_CUR_DSCR_LSB); if (!zdma_load_descriptor(s, src_addr, &s->dsc_src)) { - ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_SRC_DSCR, true); + ARRAY_FIELD_DP32(s->regs_intr, ZDMA_CH_ISR, AXI_RD_SRC_DSCR, true); } } @@ -397,7 +400,7 @@ static void zdma_load_dst_descriptor(XlnxZDMABase *s) dst_addr = zdma_get_regaddr64(s, R_ZDMA_CH_DST_CUR_DSCR_LSB); if (!zdma_load_descriptor(s, dst_addr, &s->dsc_dst)) { - ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_DST_DSCR, true); + ARRAY_FIELD_DP32(s->regs_intr, ZDMA_CH_ISR, AXI_RD_DST_DSCR, true); } /* Advance the descriptor pointer. */ @@ -534,7 +537,7 @@ static void zdma_process_descr(XlnxZDMABase *s) src_size -= len; } - ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_DONE, true); + ARRAY_FIELD_DP32(s->regs_intr, ZDMA_CH_ISR, DMA_DONE, true); if (src_intr) { zdma_src_done(s); @@ -547,8 +550,8 @@ static void zdma_process_descr(XlnxZDMABase *s) if (src_cmd == CMD_HALT) { zdma_set_state(s, PAUSED); - ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_PAUSE, 1); - ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_DONE, false); + ARRAY_FIELD_DP32(s->regs_intr, ZDMA_CH_ISR, DMA_PAUSE, 1); + ARRAY_FIELD_DP32(s->regs_intr, ZDMA_CH_ISR, DMA_DONE, false); zdma_ch_imr_update_irq(s); return; } @@ -619,20 +622,6 @@ static void zdma_ch_ctrlx_postw(RegisterInfo *reg, uint64_t val64) static RegisterAccessInfo zdma_regs_info[] = { { .name = "ZDMA_ERR_CTRL", .addr = A_ZDMA_ERR_CTRL, .rsvd = 0xfffffffe, - },{ .name = "ZDMA_CH_ISR", .addr = A_ZDMA_CH_ISR, - .rsvd = 0xffff8000, - .w1c = 0x7fff, - .post_write = zdma_ch_isr_postw, - },{ .name = "ZDMA_CH_IMR", .addr = A_ZDMA_CH_IMR, - .reset = 0x7fff, - .rsvd = 0xffff8000, - .ro = 0x7fff, - },{ .name = "ZDMA_CH_IEN", .addr = A_ZDMA_CH_IEN, - .rsvd = 0xffff8000, - .pre_write = zdma_ch_ien_prew, - },{ .name = "ZDMA_CH_IDS", .addr = A_ZDMA_CH_IDS, - .rsvd = 0xffff8000, - .pre_write = zdma_ch_ids_prew, },{ .name = "ZDMA_CH_CTRL0", .addr = A_ZDMA_CH_CTRL0, .reset = 0x80, .rsvd = 0xffffff01, @@ -724,6 +713,24 @@ static RegisterAccessInfo zdma_regs_info[] = { } }; +static RegisterAccessInfo zdma_intr_regs_info[] = { + { .name = "ZDMA_CH_ISR", .addr = A_ZDMA_CH_ISR, + .rsvd = 0xffff8000, + .w1c = 0x7fff, + .post_write = zdma_ch_isr_postw, + },{ .name = "ZDMA_CH_IMR", .addr = A_ZDMA_CH_IMR, + .reset = 0x7fff, + .rsvd = 0xffff8000, + .ro = 0x7fff, + },{ .name = "ZDMA_CH_IEN", .addr = A_ZDMA_CH_IEN, + .rsvd = 0xffff8000, + .pre_write = zdma_ch_ien_prew, + },{ .name = "ZDMA_CH_IDS", .addr = A_ZDMA_CH_IDS, + .rsvd = 0xffff8000, + .pre_write = zdma_ch_ids_prew, + } +}; + static void zdma_reset(DeviceState *dev) { XlnxZDMA *sv1 = XLNX_ZDMA(dev); @@ -734,7 +741,8 @@ static void zdma_reset(DeviceState *dev) register_reset(&sv1->regs_info[i]); } - s->regs[R_ZDMA_CH_IMR] = zdma_non_parity_mask(s, s->regs[R_ZDMA_CH_IMR]); + s->regs_intr[R_ZDMA_CH_IMR] = + zdma_non_parity_mask(s, s->regs_intr[R_ZDMA_CH_IMR]); zdma_ch_imr_update_irq(s); } @@ -749,7 +757,7 @@ static uint64_t zdma_read(void *opaque, hwaddr addr, unsigned size) qemu_log_mask(LOG_GUEST_ERROR, "%s: Decode error: read from %" HWADDR_PRIx "\n", path, addr); g_free(path); - ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true); + ARRAY_FIELD_DP32(s->regs_intr, ZDMA_CH_ISR, INV_APB, true); zdma_ch_imr_update_irq(s); return 0; } @@ -767,7 +775,7 @@ static void zdma_write(void *opaque, hwaddr addr, uint64_t value, qemu_log_mask(LOG_GUEST_ERROR, "%s: Decode error: write to %" HWADDR_PRIx "=%" PRIx64 "\n", path, addr, value); g_free(path); - ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true); + ARRAY_FIELD_DP32(s->regs_intr, ZDMA_CH_ISR, INV_APB, true); zdma_ch_imr_update_irq(s); return; } @@ -846,10 +854,15 @@ static void zdma_realize(DeviceState *dev, Error **errp) return; } + s->regs_intr = &sv1->regs[ZDMA_V1_INTR_OFFSET / 4]; s->regs = sv1->regs; zdma_populate_regs(dev, sv1->regs_info, sv1->regs, s->reg_array, zdma_regs_info, ARRAY_SIZE(zdma_regs_info), 0x0); + + zdma_populate_regs(dev, sv1->regs_info, sv1->regs, s->reg_array, + zdma_intr_regs_info, ARRAY_SIZE(zdma_intr_regs_info), + ZDMA_V1_INTR_OFFSET); } static void zdma_init(Object *obj) diff --git a/include/hw/dma/xlnx-zdma.h b/include/hw/dma/xlnx-zdma.h index d4552ea0101..29a56634c02 100644 --- a/include/hw/dma/xlnx-zdma.h +++ b/include/hw/dma/xlnx-zdma.h @@ -36,6 +36,8 @@ #define ZDMA_R_MAX (0x204 / 4) +#define ZDMA_V1_INTR_OFFSET 0x100 + typedef enum { DISABLED = 0, ENABLED = 1, @@ -76,6 +78,7 @@ struct XlnxZDMABase { uint8_t buf[2048]; uint32_t *regs; + uint32_t *regs_intr; }; struct XlnxZDMA { From dbe01091921f7c9a0c502dc530721059b67f3f06 Mon Sep 17 00:00:00 2001 From: Shiva sagar Myana Date: Wed, 3 Sep 2025 19:46:32 +0530 Subject: [PATCH 13/22] hw/dma/xlnx-zdma: Introduce the ZDMA V2 model Add support for the ZDMA V2 model. Key updates from the V1 model include: * Introduction of error status registers. * Updated register offset of interrupt registers. Signed-off-by: Shiva sagar Myana Signed-off-by: Francisco Iglesias --- hw/dma/xlnx-zdma.c | 151 +++++++++++++++++++++++++++++++++++++ include/hw/dma/xlnx-zdma.h | 11 +++ 2 files changed, 162 insertions(+) diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c index b4de965a9c8..0fe7d01fd4f 100644 --- a/hw/dma/xlnx-zdma.c +++ b/hw/dma/xlnx-zdma.c @@ -195,6 +195,52 @@ REG32(ZDMA_CH_IDS, 0x0c) FIELD(ZDMA_CH_IDS, INV_APB, 0, 1) #define ZDMA_R_INTR (R_ZDMA_CH_IDS + 1) +/* Versal Gen 2 Error registers */ +REG32(ZDMA_ERR_STS, 0x100) + FIELD(ZDMA_ERR_STS, REG_PAR_MON_ERR1, 9, 1) + FIELD(ZDMA_ERR_STS, REG_PAR_MON_ERR0, 8, 1) + FIELD(ZDMA_ERR_STS, CRC_ERR, 7, 1) + FIELD(ZDMA_ERR_STS, LINK_LIST_PERR, 6, 1) + FIELD(ZDMA_ERR_STS, FREE_LIST_PERR, 5, 1) + FIELD(ZDMA_ERR_STS, WRBUF_PERR, 4, 1) + FIELD(ZDMA_ERR_STS, AXI_WR_DATA, 3, 1) + FIELD(ZDMA_ERR_STS, AXI_RD_DATA, 2, 1) + FIELD(ZDMA_ERR_STS, AXI_RD_DST_DSCR, 1, 1) + FIELD(ZDMA_ERR_STS, AXI_RD_SRC_DSCR, 0, 1) +REG32(ZDMA_ERR_MASK, 0x104) + FIELD(ZDMA_ERR_MASK, REG_PAR_MON_ERR1, 9, 1) + FIELD(ZDMA_ERR_MASK, REG_PAR_MON_ERR0, 8, 1) + FIELD(ZDMA_ERR_MASK, CRC_ERR, 7, 1) + FIELD(ZDMA_ERR_MASK, LINK_LIST_PERR, 6, 1) + FIELD(ZDMA_ERR_MASK, FREE_LIST_PERR, 5, 1) + FIELD(ZDMA_ERR_MASK, WRBUF_PERR, 4, 1) + FIELD(ZDMA_ERR_MASK, AXI_WR_DATA, 3, 1) + FIELD(ZDMA_ERR_MASK, AXI_RD_DATA, 2, 1) + FIELD(ZDMA_ERR_MASK, AXI_RD_DST_DSCR, 1, 1) + FIELD(ZDMA_ERR_MASK, AXI_RD_SRC_DSCR, 0, 1) +REG32(ZDMA_ERR_EN, 0x108) + FIELD(ZDMA_ERR_EN, REG_PAR_MON_ERR1, 9, 1) + FIELD(ZDMA_ERR_EN, REG_PAR_MON_ERR0, 8, 1) + FIELD(ZDMA_ERR_EN, CRC_ERR, 7, 1) + FIELD(ZDMA_ERR_EN, LINK_LIST_PERR, 6, 1) + FIELD(ZDMA_ERR_EN, FREE_LIST_PERR, 5, 1) + FIELD(ZDMA_ERR_EN, WRBUF_PERR, 4, 1) + FIELD(ZDMA_ERR_EN, AXI_WR_DATA, 3, 1) + FIELD(ZDMA_ERR_EN, AXI_RD_DATA, 2, 1) + FIELD(ZDMA_ERR_EN, AXI_RD_DST_DSCR, 1, 1) + FIELD(ZDMA_ERR_EN, AXI_RD_SRC_DSCR, 0, 1) +REG32(ZDMA_ERR_DIS, 0x10c) + FIELD(ZDMA_ERR_DIS, REG_PAR_MON_ERR1, 9, 1) + FIELD(ZDMA_ERR_DIS, REG_PAR_MON_ERR0, 8, 1) + FIELD(ZDMA_ERR_DIS, CRC_ERR, 7, 1) + FIELD(ZDMA_ERR_DIS, LINK_LIST_PERR, 6, 1) + FIELD(ZDMA_ERR_DIS, FREE_LIST_PERR, 5, 1) + FIELD(ZDMA_ERR_DIS, WRBUF_PERR, 4, 1) + FIELD(ZDMA_ERR_DIS, AXI_WR_DATA, 3, 1) + FIELD(ZDMA_ERR_DIS, AXI_RD_DATA, 2, 1) + FIELD(ZDMA_ERR_DIS, AXI_RD_DST_DSCR, 1, 1) + FIELD(ZDMA_ERR_DIS, AXI_RD_SRC_DSCR, 0, 1) + enum { PT_REG = 0, PT_MEM = 1, @@ -731,6 +777,21 @@ static RegisterAccessInfo zdma_intr_regs_info[] = { } }; +static RegisterAccessInfo zdma_err_regs_info[] = { + { .name = "ZDMA_ERR_STS", .addr = A_ZDMA_ERR_STS, + .rsvd = 0xfffffc00, + .w1c = 0x3ff, + },{ .name = "ZDMA_ERR_MASK", .addr = A_ZDMA_ERR_MASK, + .reset = 0x3ff, + .rsvd = 0xfffffc00, + .ro = 0x3ff, + },{ .name = "ZDMA_ERR_EN", .addr = A_ZDMA_ERR_EN, + .rsvd = 0xfffffc00, + },{ .name = "ZDMA_ERR_DIS", .addr = A_ZDMA_ERR_DIS, + .rsvd = 0xfffffc00, + } +}; + static void zdma_reset(DeviceState *dev) { XlnxZDMA *sv1 = XLNX_ZDMA(dev); @@ -747,6 +808,21 @@ static void zdma_reset(DeviceState *dev) zdma_ch_imr_update_irq(s); } +static void zdma_v2_reset(DeviceState *dev) +{ + XlnxZDMAV2 *sv2 = XLNX_ZDMA_V2(dev); + XlnxZDMABase *s = XLNX_ZDMA_BASE(dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sv2->regs_info); ++i) { + register_reset(&sv2->regs_info[i]); + } + s->regs_intr[R_ZDMA_CH_IMR] = + zdma_non_parity_mask(s, s->regs_intr[R_ZDMA_CH_IMR]); + + zdma_ch_imr_update_irq(s); +} + static uint64_t zdma_read(void *opaque, hwaddr addr, unsigned size) { XlnxZDMABase *s = XLNX_ZDMA_BASE(opaque); @@ -865,6 +941,31 @@ static void zdma_realize(DeviceState *dev, Error **errp) ZDMA_V1_INTR_OFFSET); } +static void zdma_v2_realize(DeviceState *dev, Error **errp) +{ + XlnxZDMAV2 *sv2 = XLNX_ZDMA_V2(dev); + XlnxZDMABase *s = XLNX_ZDMA_BASE(dev); + + zdma_common_realize(dev, errp); + if (*errp) { + return; + } + + s->regs_intr = &sv2->regs[ZDMA_V2_INTR_OFFSET / 4]; + s->regs = sv2->regs; + + zdma_populate_regs(dev, sv2->regs_info, sv2->regs, s->reg_array, + zdma_regs_info, ARRAY_SIZE(zdma_regs_info), 0x0); + + zdma_populate_regs(dev, sv2->regs_info, sv2->regs, s->reg_array, + zdma_intr_regs_info, ARRAY_SIZE(zdma_intr_regs_info), + ZDMA_V2_INTR_OFFSET); + + zdma_populate_regs(dev, sv2->regs_info, sv2->regs, s->reg_array, + zdma_err_regs_info, + ARRAY_SIZE(zdma_err_regs_info), 0x0); +} + static void zdma_init(Object *obj) { XlnxZDMABase *s = XLNX_ZDMA_BASE(obj); @@ -883,6 +984,24 @@ static void zdma_init(Object *obj) OBJ_PROP_LINK_STRONG); } +static void zdma_v2_init(Object *obj) +{ + XlnxZDMABase *s = XLNX_ZDMA_BASE(obj); + XlnxZDMAV2 *sv2 = XLNX_ZDMA_V2(obj); + + s->reg_array = g_new0(RegisterInfoArray, 1); + s->reg_array->r = g_new0(RegisterInfo *, ZDMA_V2_R_MAX); + s->reg_array->num_elements = ZDMA_V2_R_MAX; + + memory_region_init_io(&s->reg_array->mem, OBJECT(sv2), &zdma_ops, sv2, + TYPE_XLNX_ZDMA_V2, ZDMA_V2_R_MAX * 4); + + object_property_add_link(obj, "memattr", TYPE_MEMORY_TRANSACTION_ATTR, + (Object **)&s->attr_ptr, + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG); +} + static const VMStateDescription vmstate_zdma = { .name = TYPE_XLNX_ZDMA, .version_id = 1, @@ -896,6 +1015,19 @@ static const VMStateDescription vmstate_zdma = { } }; +static const VMStateDescription vmstate_zdma_v2 = { + .name = TYPE_XLNX_ZDMA_V2, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, XlnxZDMAV2, ZDMA_V2_R_MAX), + VMSTATE_UINT32(parent_obj.state, XlnxZDMAV2), + VMSTATE_UINT32_ARRAY(parent_obj.dsc_src.words, XlnxZDMAV2, 4), + VMSTATE_UINT32_ARRAY(parent_obj.dsc_dst.words, XlnxZDMAV2, 4), + VMSTATE_END_OF_LIST(), + } +}; + static Property zdma_props[] = { DEFINE_PROP_UINT32("bus-width", XlnxZDMABase, cfg.bus_width, 64), DEFINE_PROP_LINK("dma", XlnxZDMABase, dma_mr, @@ -914,6 +1046,16 @@ static void zdma_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_zdma; } +static void zdma_v2_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = zdma_v2_realize; + dc->reset = zdma_v2_reset; + device_class_set_props(dc, zdma_props); + dc->vmsd = &vmstate_zdma_v2; +} + static const TypeInfo zdma_base_info = { .name = TYPE_XLNX_ZDMA_BASE, .parent = TYPE_SYS_BUS_DEVICE, @@ -929,10 +1071,19 @@ static const TypeInfo zdma_info = { .instance_init = zdma_init, }; +static const TypeInfo zdma_v2_info = { + .name = TYPE_XLNX_ZDMA_V2, + .parent = TYPE_XLNX_ZDMA_BASE, + .instance_size = sizeof(XlnxZDMAV2), + .class_init = zdma_v2_class_init, + .instance_init = zdma_v2_init, +}; + static void zdma_register_types(void) { type_register_static(&zdma_base_info); type_register_static(&zdma_info); + type_register_static(&zdma_v2_info); } type_init(zdma_register_types) diff --git a/include/hw/dma/xlnx-zdma.h b/include/hw/dma/xlnx-zdma.h index 29a56634c02..41723e8a4e8 100644 --- a/include/hw/dma/xlnx-zdma.h +++ b/include/hw/dma/xlnx-zdma.h @@ -35,8 +35,11 @@ #include "qom/object.h" #define ZDMA_R_MAX (0x204 / 4) +#define ZDMA_V2_R_MAX (0x41c / 4) +#define ZDMA_INTR_R_MAX (0x10 / 4) #define ZDMA_V1_INTR_OFFSET 0x100 +#define ZDMA_V2_INTR_OFFSET 0x408 typedef enum { DISABLED = 0, @@ -86,13 +89,21 @@ struct XlnxZDMA { uint32_t regs[ZDMA_R_MAX]; RegisterInfo regs_info[ZDMA_R_MAX]; +}; + +struct XlnxZDMAV2 { + struct XlnxZDMABase parent_obj; + uint32_t regs[ZDMA_V2_R_MAX]; + RegisterInfo regs_info[ZDMA_V2_R_MAX]; }; #define TYPE_XLNX_ZDMA "xlnx.zdma" #define TYPE_XLNX_ZDMA_BASE "xlnx-zdma-base" +#define TYPE_XLNX_ZDMA_V2 "xlnx.zdma-v2" OBJECT_DECLARE_SIMPLE_TYPE(XlnxZDMA, XLNX_ZDMA) OBJECT_DECLARE_SIMPLE_TYPE(XlnxZDMABase, XLNX_ZDMA_BASE) +OBJECT_DECLARE_SIMPLE_TYPE(XlnxZDMAV2, XLNX_ZDMA_V2) #endif /* XLNX_ZDMA_H */ From 8b74e8a35ae2c529c23c66deb5fb7684e1fd7252 Mon Sep 17 00:00:00 2001 From: Shiva sagar Myana Date: Tue, 23 Sep 2025 17:11:35 +0530 Subject: [PATCH 14/22] hw/misc/xlnx-trng1-r2: return internal raw entropy in EU mode If EU mode is enabled, the TRNG returns internal raw entropy values to the core_output instead of pseudo-random numbers, as the PRNG unit is disabled in this mode. Signed-off-by: Shiva sagar Myana Signed-off-by: Francisco Iglesias --- hw/misc/xlnx-trng1-r2.c | 68 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/hw/misc/xlnx-trng1-r2.c b/hw/misc/xlnx-trng1-r2.c index 35124b9bae9..d5bb58d2b92 100644 --- a/hw/misc/xlnx-trng1-r2.c +++ b/hw/misc/xlnx-trng1-r2.c @@ -159,9 +159,7 @@ static bool xlnx_trng1r2_tst_mode(XlnxTRng1r2 *s) static bool xlnx_trng1r2_eu_mode(XlnxTRng1r2 *s) { - /* Supported only in TSTMODE to read back injected entropy */ - return ARRAY_FIELD_EX32(s->regs, CTRL, EUMODE) - && xlnx_trng1r2_tst_mode(s); + return ARRAY_FIELD_EX32(s->regs, CTRL, EUMODE); } static bool xlnx_trng1r2_gen_mode(XlnxTRng1r2 *s) @@ -279,7 +277,7 @@ static uint32_t xlnx_trng1r2_tstent_u32(XlnxTRng1r2 *s) { size_t i, o; - if (!xlnx_trng1r2_eu_mode(s)) { + if (!xlnx_trng1r2_tst_mode(s)) { return 0; } @@ -312,14 +310,14 @@ static void xlnx_trng1r2_tstent_collect(XlnxTRng1r2 *s) s->entropy.test_input_vld = 0; /* Indicate available for readback */ - if (xlnx_trng1r2_eu_mode(s)) { + if (xlnx_trng1r2_tst_mode(s) && xlnx_trng1r2_eu_mode(s)) { xlnx_trng1r2_set_wcnt(s, (s->entropy.test_input->len / 4)); } } static void xlnx_trng1r2_tstent_clr(XlnxTRng1r2 *s) { - if (xlnx_trng1r2_eu_mode(s)) { + if (xlnx_trng1r2_tst_mode(s)) { xlnx_trng1r2_set_wcnt(s, 0); } @@ -505,12 +503,63 @@ static uint32_t xlnx_trng1r2_get32(XlnxTRng1r2 *s) return n; } +static void xlnx_trng1r2_eumode_clear(XlnxTRng1r2 *s) +{ + s->entropy.test_output = 0; + if (s->entropy.test_input) { + g_autoptr(GArray) eu = s->entropy.test_input; + s->entropy.test_input = NULL; + } + + ARRAY_FIELD_DP32(s->regs, STATUS, QCNT, 0); + xlnx_trng1r2_clr_done(s); +} + +static void xlnx_trng1r2_eumode_refill(XlnxTRng1r2 *s) +{ + g_autoptr(GArray) eu_entropy = xlnx_trng1r2_entropy(s); + if (eu_entropy->len >= 16) { + xlnx_trng1r2_eumode_clear(s); + + s->entropy.test_input = g_array_new(false, false, 1); + g_array_append_vals(s->entropy.test_input, eu_entropy->data, 16); + + ARRAY_FIELD_DP32(s->regs, STATUS, QCNT, 4); + xlnx_trng1r2_set_done(s); + } +} + +static uint32_t xlnx_trng1r2_eumode_u32(XlnxTRng1r2 *s) +{ + uint32_t n; + + if (s->entropy.test_input && s->entropy.test_input->len >= 4) { + n = ldl_be_p(s->entropy.test_input->data); + g_array_remove_range(s->entropy.test_input, 0, 4); + + xlnx_trng1r2_set_wcnt(s, (s->entropy.test_input->len / 4)); + + /* The core continues to collect the raw entropy data and refill */ + if (s->entropy.test_input->len == 0) { + xlnx_trng1r2_eumode_refill(s); + } + return n; + } + return 0; +} + static uint32_t xlnx_trng1r2_core_output(XlnxTRng1r2 *s) { uint32_t n; if (xlnx_trng1r2_eu_mode(s)) { - return xlnx_trng1r2_tstent_u32(s); + if (xlnx_trng1r2_tst_mode(s)) { + /* Return injected entropy in TSTmode */ + return xlnx_trng1r2_tstent_u32(s); + } else { + /* Return raw entropy data */ + return xlnx_trng1r2_eumode_u32(s); + } } if (xlnx_trng1r2_is_nonstop(s)) { @@ -655,8 +704,11 @@ static void xlnx_trng1r2_ctrl_updated(XlnxTRng1r2 *s, if (FIELD_EX32(to_0s, CTRL, TSTMODE)) { xlnx_trng1r2_tstent_clr(s); } + if (xlnx_trng1r2_eu_mode(s) && !ARRAY_FIELD_EX32(s->regs, CTRL, TSTMODE)) { + xlnx_trng1r2_eumode_refill(s); + } if (FIELD_EX32(to_0s, CTRL, EUMODE)) { - xlnx_trng1r2_set_wcnt(s, 0); + xlnx_trng1r2_eumode_clear(s); } /* Any toggle is a potential source causing STATUS.DONE to be cleared */ From 576ef19ecbb19c0ec901ff516e45cd702d377428 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Tue, 23 Sep 2025 16:06:49 +0200 Subject: [PATCH 15/22] hw/ssi/xilinx_spips: mask TX_THRES and RX_THRES written values TX_THRES and RX_THRES sized are according the corresponding hardware FIFO size configuration. Unimplemented MSBs are RAZ/WI. Drivers use this property to detect the FIFO size. Mask out the written value to ensure this. Signed-off-by: Luc Michel Signed-off-by: Francisco Iglesias --- hw/ssi/xilinx_spips.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index 99b680ebe8e..997cb61edf5 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -986,6 +986,7 @@ static void xilinx_spips_write(void *opaque, hwaddr addr, int mask = ~0; XilinxSPIPS *s = opaque; bool try_flush = true; + XilinxSPIPSClass *xspic = XILINX_SPIPS_GET_CLASS(s); DB_PRINT_L(0, "addr=" HWADDR_FMT_plx " = %x\n", addr, (unsigned)value); addr >>= 2; @@ -1016,6 +1017,12 @@ static void xilinx_spips_write(void *opaque, hwaddr addr, case R_SLAVE_IDLE_COUNT: mask = 0xFF; break; + case R_TX_THRES: + mask = xspic->tx_fifo_size - 1; + break; + case R_RX_THRES: + mask = xspic->rx_fifo_size - 1; + break; case R_RX_DATA: case R_INTR_MASK: case R_MOD_ID: From c8951c61a50cb215c4d808fbe87f45aa6609bb05 Mon Sep 17 00:00:00 2001 From: Khem Raj Date: Fri, 7 Feb 2025 16:09:20 +0000 Subject: [PATCH 16/22] linux-user: Do not define struct sched_attr if libc headers do glibc 2.41+ has added [1] definitions for sched_setattr and sched_getattr functions and struct sched_attr. Therefore, it needs to be checked for here as well before defining sched_attr, to avoid a compilation failure. Define sched_attr conditionally only when SCHED_ATTR_SIZE_VER0 is not defined. [1] https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=21571ca0d70302909cf72707b2a7736cf12190a0;hp=298bc488fdc047da37482f4003023cb9adef78f8 Signed-off-by: Khem Raj Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2799 Cc: qemu-stable@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- linux-user/syscall.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index b4cb5c86af2..dbcd45be797 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -359,7 +359,8 @@ _syscall3(int, sys_sched_getaffinity, pid_t, pid, unsigned int, len, #define __NR_sys_sched_setaffinity __NR_sched_setaffinity _syscall3(int, sys_sched_setaffinity, pid_t, pid, unsigned int, len, unsigned long *, user_mask_ptr); -/* sched_attr is not defined in glibc */ +/* sched_attr is not defined in glibc < 2.41 */ +#ifndef SCHED_ATTR_SIZE_VER0 struct sched_attr { uint32_t size; uint32_t sched_policy; @@ -372,6 +373,7 @@ struct sched_attr { uint32_t sched_util_min; uint32_t sched_util_max; }; +#endif #define __NR_sys_sched_getattr __NR_sched_getattr _syscall4(int, sys_sched_getattr, pid_t, pid, struct sched_attr *, attr, unsigned int, size, unsigned int, flags); From 5c09f791a9116a26f29224d6372d6811f57c99c3 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Thu, 25 Sep 2025 15:18:54 +0200 Subject: [PATCH 17/22] hw/core/fdt_generic_util: remove unnecessary memset Calling memset on a local variable is awkward, and this particular call is unnecessary because the variable gets initialized after in all branches of the control flow. Drop the memset. Signed-off-by: Luc Michel Signed-off-by: Francisco Iglesias --- hw/core/fdt_generic_util.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c index 480cb23d79f..d650aad7fd8 100644 --- a/hw/core/fdt_generic_util.c +++ b/hw/core/fdt_generic_util.c @@ -2062,7 +2062,6 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat) const char *gpio_name = NULL; uint16_t named_idx = 0; qemu_irq input, output; - memset(&input, 0, sizeof(input)); if (c) { uint16_t range = c->range ? c->range : 1; From b9bca6681fd94413a7a233d0c3fe13709a81f6f3 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Thu, 25 Sep 2025 15:18:55 +0200 Subject: [PATCH 18/22] hw/intc/xilinx-zynqmp-intc-redirect: fix overflowing shift i varies from 0 to 63. Fix the shift accordingly. Signed-off-by: Luc Michel Signed-off-by: Francisco Iglesias --- hw/intc/xilinx-zynqmp-intc-redirect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/xilinx-zynqmp-intc-redirect.c b/hw/intc/xilinx-zynqmp-intc-redirect.c index d9216923ca2..6c62d67edca 100644 --- a/hw/intc/xilinx-zynqmp-intc-redirect.c +++ b/hw/intc/xilinx-zynqmp-intc-redirect.c @@ -70,7 +70,7 @@ static void intc_redirect_update_irqs(void *opaque) /* Always propagate IRQs between GIC and APU. */ for (i = 0; i < NUM_LINES_FROM_GIC; i++) { - qemu_set_irq(s->cpu_out[i], !!(s->irq_in & (1U << i))); + qemu_set_irq(s->cpu_out[i], !!(s->irq_in & (1ULL << i))); } } From 3f75375bf00ea0fb233482c9081a3826f43f3815 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Thu, 25 Sep 2025 15:18:56 +0200 Subject: [PATCH 19/22] hw/ssi/xilinx_spips: fix possible stack overflow xilinx_qspips_write puts a 1MiB buffer on the stack. This can easily triggers a stack overflow depending on the call stack. Fix that by allocating the buffer instead. Signed-off-by: Luc Michel Signed-off-by: Francisco Iglesias --- hw/ssi/xilinx_spips.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index 997cb61edf5..89a7f83bce9 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -1092,13 +1092,15 @@ static void xilinx_qspips_write(void *opaque, hwaddr addr, uint32_t src = q->lqspi_src; uint32_t dst = q->lqspi_dst; uint32_t btt = q->lqspi_size; + g_autofree uint8_t *lqspi_hack_buf = NULL; + + lqspi_hack_buf = g_malloc(LQSPI_HACK_CHUNK_SIZE); assert(!(btt % LQSPI_HACK_CHUNK_SIZE)); fprintf(stderr, "QEMU: Syncing LQSPI - this may be slow " "(1 \".\" / MByte):"); while (btt) { - uint8_t lqspi_hack_buf[LQSPI_HACK_CHUNK_SIZE]; dma_memory_read(q->hack_as, src, lqspi_hack_buf, LQSPI_HACK_CHUNK_SIZE, MEMTXATTRS_UNSPECIFIED); dma_memory_write(q->hack_as, dst, lqspi_hack_buf, From b3eb69a7916c5af6cdd6d7edd20bf9d1ebdf542b Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Thu, 25 Sep 2025 15:18:57 +0200 Subject: [PATCH 20/22] include/hw/fdt_generic_util: add missing sentinels in default_gpio_sets The gpios sections of the default_gpio_sets array were missing the sentinels. Add them. Signed-off-by: Luc Michel Signed-off-by: Francisco Iglesias --- include/hw/fdt_generic_util.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/hw/fdt_generic_util.h b/include/hw/fdt_generic_util.h index 3cf7173ed57..961f0dc41bc 100644 --- a/include/hw/fdt_generic_util.h +++ b/include/hw/fdt_generic_util.h @@ -198,18 +198,21 @@ static const FDTGenericGPIOSet default_gpio_sets [] = { .names = &fdt_generic_gpio_name_set_reset_gpio, .gpios = (FDTGenericGPIOConnection[]) { { .name = "rst_cntrl", .fdt_index = 0, .range = 6 }, + { }, }, }, { .names = &fdt_generic_gpio_name_set_resets, .gpios = (FDTGenericGPIOConnection[]) { { .name = "rst_cntrl", .fdt_index = 0, .range = 6 }, + { }, }, }, { .names = &fdt_generic_gpio_name_set_power_gpio, .gpios = (FDTGenericGPIOConnection[]) { { .name = "pwr_cntrl", .fdt_index = 0, .range = 1 }, + { }, }, }, { .names = &fdt_generic_gpio_name_set_interrupts }, From 47e8337cba76ea6352dccd8e6dd18cae8cf48b4e Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Thu, 25 Sep 2025 15:18:58 +0200 Subject: [PATCH 21/22] hw/mdio/eth_phy: fix register array length The number of PHY registers is 32, from 0 to 31. Fix this. Signed-off-by: Luc Michel Signed-off-by: Francisco Iglesias --- include/hw/mdio/eth_phy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/mdio/eth_phy.h b/include/hw/mdio/eth_phy.h index 46bc8c6a9c3..d7cc0f44f4e 100644 --- a/include/hw/mdio/eth_phy.h +++ b/include/hw/mdio/eth_phy.h @@ -46,7 +46,7 @@ #define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ #define PHY_SPEC_CTRL 0x10 /* PHY Specific control reg */ #define PHY_SPEC_STATUS 0x11 /* PHY Specific status reg */ -#define NUM_PHY_REGS 0x1F /* 5 bit address bus (0-0x1F) */ +#define NUM_PHY_REGS 32 /* 5 bit address bus (0-0x1F) */ /*Control Register bitfeild offsets*/ #define PHY_CTRL_REG_UNIDIR_EN 5 From 3189c17cdc7d0982d2c4ed4505ad3c441a565b3d Mon Sep 17 00:00:00 2001 From: Doug Brown Date: Thu, 25 Sep 2025 19:07:44 +0530 Subject: [PATCH 22/22] hw/net/can/xlnx-versal-canfd: Fix interrupt level The interrupt level should be 0 or 1. The existing code was using the interrupt flags to determine the level. In the only machine currently supported (xlnx-versal-virt), the GICv3 was masking off all bits except bit 0 when applying it, resulting in the IRQ never being delivered. Signed-off-by: Doug Brown Reviewed-by: Francisco Iglesias Reviewed-by: Pavel Pisa Message-id: 20240827034927.66659-2-doug@schmorgal.com Signed-off-by: Peter Maydell Signed-off-by: Francisco Iglesias --- hw/net/can/xlnx-versal-canfd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/net/can/xlnx-versal-canfd.c b/hw/net/can/xlnx-versal-canfd.c index 9dbd01810b2..07a21ae793d 100644 --- a/hw/net/can/xlnx-versal-canfd.c +++ b/hw/net/can/xlnx-versal-canfd.c @@ -682,8 +682,8 @@ static uint8_t canfd_dlc_array[8] = {8, 12, 16, 20, 24, 32, 48, 64}; static void canfd_update_irq(XlnxVersalCANFDState *s) { - unsigned int irq = s->regs[R_INTERRUPT_STATUS_REGISTER] & - s->regs[R_INTERRUPT_ENABLE_REGISTER]; + const bool irq = (s->regs[R_INTERRUPT_STATUS_REGISTER] & + s->regs[R_INTERRUPT_ENABLE_REGISTER]) != 0; g_autofree char *path = object_get_canonical_path(OBJECT(s)); /* RX watermark interrupts. */