:p
atchew
Login
Hi; here's this week's arm pullreq; lots of smmuv3 related work here, plus some initial work towards emulating FEAT_SVE2p2. thanks -- PMM The following changes since commit 2f28d34ea0aead9830478cd1d3d0dd9d9191d82e: Merge tag 'pull-tcg-20260612' of https://gitlab.com/rth7680/qemu into staging (2026-06-13 14:02:34 -0400) are available in the Git repository at: https://gitlab.com/pm215/qemu.git tags/pull-target-arm-20260616 for you to fetch changes up to 8de1ba58af636d1844be43b9ea6e34ced302a057: target/arm: Implement floating-point log and convert to integer (zeroing) (2026-06-16 16:54:25 +0100) ---------------------------------------------------------------- target-arm queue: * Implementation of various insns preparatory to FEAT_SVE2p2 * hw/arm/smmuv3: Make smmuv3 ATS, RIL, SSIDSIZE, and OAS 'auto' properties work * hw/pci/pci: Enforce pci_setup_iommu_per_bus() is called only once per bus * hw/arm/virt: Introduce Tegra241 CMDQV support for accelerated SMMUv3 * target/arm: honour CCR.BFHFNMIGN for probed data BusFaults * hw/arm/bcm2838: Route I2C interrupts to GIC ---------------------------------------------------------------- Eric Auger (1): hw/pci/pci: Enforce pci_setup_iommu_per_bus() is called only once per bus Kyle Fox (1): target/arm: honour CCR.BFHFNMIGN for probed data BusFaults Nathan Chen (9): hw/arm/smmuv3: Update ATC invalidation check hw/arm/smmuv3: Improve accel SMMUv3 usage documentation hw/arm/smmuv3-accel: Add helper for resolving auto parameters hw/arm/smmuv3-accel: Implement "auto" value for "ats" hw/arm/smmuv3-accel: Implement "auto" value for "ril" hw/arm/smmuv3-accel: Implement "auto" value for "ssidsize" hw/arm/smmuv3-accel: Implement "auto" value for "oas" hw/arm/smmuv3: Set default ats, ril, ssidsize, oas to auto qemu-options.hx: Support "auto" for accel SMMUv3 properties Nicholas Righi (1): hw/arm/bcm2838: Route I2C interrupts to GIC Nicolin Chen (15): backends/iommufd: Update iommufd_backend_get_device_info backends/iommufd: Update iommufd_backend_alloc_viommu to allow user ptr backends/iommufd: Introduce iommufd_backend_alloc_hw_queue backends/iommufd: Introduce iommufd_backend_viommu_mmap hw/arm/tegra241-cmdqv: Implement CMDQV init hw/arm/tegra241-cmdqv: Implement CMDQV vIOMMU alloc/free hw/arm/tegra241-cmdqv: mmap host VINTF Page0 for CMDQV hw/arm/tegra241-cmdqv: Emulate CMDQ-V Config region hw/arm/tegra241-cmdqv: Emulate VCMDQ register reads hw/arm/tegra241-cmdqv: Emulate VCMDQ register writes hw/arm/tegra241-cmdqv: Allocate HW VCMDQs once configured hw/arm/tegra241-cmdqv: Use mmap'd host VINTF page0 for virtual VINTF page0 hw/arm/tegra241-cmdqv: Initialize register state on reset hw/arm/tegra241-cmdqv: Limit queue size based on backend page size hw/arm/virt-acpi: Advertise Tegra241 CMDQV nodes in DSDT Richard Henderson (18): target/arm: Add feature predicates for SVE2.2 and SME2.2 target/arm: Rename sve unary predicated patterns target/arm: Enable zeroing in DO_ZPZ macros in sve_helper.c target/arm: Expand DO_ZPZ in translate-sve.c target/arm: Implement SVE integer unary operations (predicated, zeroing) target/arm: Implement SVE bitwise unary operations (predicated, zeroing) target/arm: Implement SVE reverse within elements (zeroing) target/arm: Implement SVE reverse doublewords (zeroing) target/arm: Implement SVE2 integer unary operations (predicated, zeroing) target/arm: Add data argument to do_frint_mode target/arm: Implement Floating-point round to integral value (predicated, zeroing) target/arm: Implement Floating-point convert (predicated, zeroing) target/arm: Implement Floating-point square root (predicated, zeroing) target/arm: Implement SCVTF, UCVTF (predicated, zeroing) target/arm: Implement FRINT{32,64}{X,Z} target/arm: Enable zeroing in DO_FCVT{N, L}T macros in sve_helper.c target/arm: Implement SVE floating-point convert (top, predicated, zeroing) target/arm: Implement floating-point log and convert to integer (zeroing) Shameer Kolothum (16): system/iommufd: Remove unused viommu pointer from IOMMUFDVeventq hw/arm/smmuv3-accel: Introduce CMDQV ops interface hw/arm/tegra241-cmdqv: Add Tegra241 CMDQV ops backend stub hw/arm/smmuv3-accel: Wire CMDQV ops into accel lifecycle hw/arm/virt: Use stored SMMUv3 device list for IORT build hw/arm/tegra241-cmdqv: Probe host Tegra241 CMDQV support hw/arm/virt: Link SMMUv3 CMDQV resources to platform bus hw/arm/tegra241-cmdqv: Route allocated VCMDQ Page0 accesses to the mmap'd host VINTF page0 memory: Allow RAM device regions to skip IOMMU mapping hw/arm/smmuv3-accel: Introduce common helper for veventq read hw/arm/tegra241-cmdqv: Read and propagate Tegra241 CMDQV errors hw/arm/smmuv3: Add per-device identifier property hw/arm/smmuv3-accel: Introduce helper to query CMDQV type hw/arm/smmuv3-accel: Enforce viommu association when CMDQV is active hw/arm/tegra241-cmdqv: Document the CMDQV design and lifecycle hw/arm/smmuv3: Add cmdqv property for SMMUv3 device backends/iommufd.c | 64 ++ backends/trace-events | 4 +- hw/arm/Kconfig | 5 + hw/arm/bcm2835_peripherals.c | 9 + hw/arm/bcm2838.c | 4 + hw/arm/meson.build | 2 + hw/arm/smmu-common.c | 4 +- hw/arm/smmuv3-accel-stubs.c | 12 + hw/arm/smmuv3-accel.c | 277 +++++++-- hw/arm/smmuv3-accel.h | 50 ++ hw/arm/smmuv3.c | 91 +-- hw/arm/tegra241-cmdqv-stubs.c | 16 + hw/arm/tegra241-cmdqv.c | 1119 ++++++++++++++++++++++++++++++++++ hw/arm/tegra241-cmdqv.h | 384 ++++++++++++ hw/arm/trace-events | 11 + hw/arm/virt-acpi-build.c | 127 ++-- hw/arm/virt.c | 37 ++ hw/core/machine.c | 5 + hw/pci/pci.c | 9 +- hw/vfio/iommufd.c | 4 +- hw/vfio/listener.c | 6 + hw/vfio/trace-events | 1 + include/hw/arm/bcm2835_peripherals.h | 2 + include/hw/arm/bcm2838_peripherals.h | 1 + include/hw/arm/smmuv3.h | 6 + include/hw/arm/virt.h | 1 + include/hw/pci/pci.h | 16 +- include/system/iommufd.h | 17 +- include/system/memory.h | 21 + qemu-options.hx | 33 +- system/memory.c | 10 + target/arm/cpu-features.h | 17 +- target/arm/tcg/helper-sve-defs.h | 9 + target/arm/tcg/m_helper.c | 16 +- target/arm/tcg/sve.decode | 261 +++++--- target/arm/tcg/sve_helper.c | 30 +- target/arm/tcg/tlb_helper.c | 24 + target/arm/tcg/translate-sve.c | 471 ++++++++++---- 38 files changed, 2861 insertions(+), 315 deletions(-) create mode 100644 hw/arm/tegra241-cmdqv-stubs.c create mode 100644 hw/arm/tegra241-cmdqv.c create mode 100644 hw/arm/tegra241-cmdqv.h
From: Nathan Chen <nathanc@nvidia.com> Use smmuv3_ats_enabled() to determine whether ATS is enabled for the guest when handling an ATC invalidation command, as setting the ATS property value to 'auto' will resolve to ATS being detected as enabled in the ATC invalidation check otherwise. Fixes: f7f5013a55a3 ("hw/arm/smmuv3-accel: Add support for ATS") Reported-by: Shameer Kolothum <skolothumtho@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Nathan Chen <nathanc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260608174900.2227340-2-nathanc@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -XXX,XX +XXX,XX @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp) { SMMUDevice *sdev = smmu_find_sdev(bs, CMD_SID(&cmd)); - if (!sdev || !s->ats) { + if (!sdev || !smmuv3_ats_enabled(s)) { trace_smmuv3_unhandled_cmd(type); break; } -- 2.43.0
From: Nathan Chen <nathanc@nvidia.com> Add a statement to clarify that the host SMMUv3 must support HW-accelerated vfio-pci device assignment when setting accel=on. Reported-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Nathan Chen <nathanc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Shameer Kolothum <skolothumtho@nvidia.com> Message-id: 20260608174900.2227340-3-nathanc@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -XXX,XX +XXX,XX @@ static void smmuv3_class_init(ObjectClass *klass, const void *data) object_class_property_set_description(klass, "accel", "Enable SMMUv3 accelerator support. Allows host SMMUv3 to be " - "configured in nested mode for vfio-pci dev assignment"); + "configured in nested mode for vfio-pci dev assignment. Please " + "ensure the host SMMUv3 supports nested translation before " + "enabling."); object_class_property_set_description(klass, "ril", "Disable range invalidation support (for accel=on). ril=auto " "is not supported."); -- 2.43.0
From: Nathan Chen <nathanc@nvidia.com> Introduce smmuv3_accel_auto_finalise() to resolve properties that are set to 'auto' for accelerated SMMUv3. This helper function allows properties such as ats, ril, ssidsize, and oas support to be resolved from host IOMMU capabilities via IOMMU_GET_HW_INFO. The later commits in this series set the auto_mode flag to true when an accel SMMUv3 property value is explicitly set to 'auto', or if the property value is not set and defaults to auto mode. Setting these property values to 'auto' requires at least one cold-plugged device to retrieve and finalise these properties. If the auto_mode flag is true, register a machine_init_done notifier to verify this requirement and fail boot if it is not met. Hot-plugged devices into an accel SMMUv3-associated bus will re-use the resolved host values from the initial cold-plug. Subsequent patches will make use of this helper to resolve 'auto' to what is reported by host IOMMU capabilities. Suggested-by: Shameer Kolothum <skolothumtho@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Nathan Chen <nathanc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260608174900.2227340-4-nathanc@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-accel.c | 41 +++++++++++++++++++++++++++++++++++++++++ hw/arm/smmuv3-accel.h | 2 ++ include/hw/arm/smmuv3.h | 3 +++ 3 files changed, 46 insertions(+) diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -XXX,XX +XXX,XX @@ #include "smmuv3-internal.h" #include "smmuv3-accel.h" +#include "system/system.h" /* * The root region aliases the global system memory, and shared_as_sysmem @@ -XXX,XX +XXX,XX @@ static int smmuv3_oas_bits(uint32_t oas) return map[oas]; } +static void smmuv3_accel_auto_finalise(SMMUv3State *s, + struct iommu_hw_info_arm_smmuv3 *info) +{ + SMMUv3AccelState *accel = s->s_accel; + + /* + * Return if 'auto' was not set for any accel SMMUv3 property, or + * if property values were already resolved from a previous call + * to this function (e.g. if this function was called again after + * VM boot during device hot plug). We do not accept new property + * values in this case where auto_finalised == true, and we re-use + * the values determined from the initial cold plug. + */ + if (!accel->auto_mode || accel->auto_finalised) { + return; + } + + accel->auto_finalised = true; +} + static bool smmuv3_accel_check_hw_compatible(SMMUv3State *s, struct iommu_hw_info_arm_smmuv3 *info, Error **errp) { + smmuv3_accel_auto_finalise(s, info); + /* QEMU SMMUv3 supports both linear and 2-level stream tables */ if (FIELD_EX32(info->idr[0], IDR0, STLEVEL) != FIELD_EX32(s->idr[0], IDR0, STLEVEL)) { @@ -XXX,XX +XXX,XX @@ static void smmuv3_accel_as_init(SMMUv3State *s) address_space_init(shared_as_sysmem, &root, "smmuv3-accel-as-sysmem"); } +static void smmuv3_accel_machine_done(Notifier *notifier, void *data) +{ + SMMUv3State *s = container_of(notifier, SMMUv3State, machine_done); + SMMUv3AccelState *accel = s->s_accel; + + if (accel->auto_mode && !accel->auto_finalised) { + error_report("arm-smmuv3 accel=on with 'auto' properties requires " + "at least one cold-plugged VFIO device"); + exit(1); + } +} + bool smmuv3_accel_init(SMMUv3State *s, Error **errp) { SMMUState *bs = ARM_SMMU(s); @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_init(SMMUv3State *s, Error **errp) s->s_accel = g_new0(SMMUv3AccelState, 1); bs->iommu_ops = &smmuv3_accel_ops; smmuv3_accel_as_init(s); + + if (s->s_accel->auto_mode) { + s->machine_done.notify = smmuv3_accel_machine_done; + qemu_add_machine_init_done_notifier(&s->machine_done); + } + return true; } diff --git a/hw/arm/smmuv3-accel.h b/hw/arm/smmuv3-accel.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.h +++ b/hw/arm/smmuv3-accel.h @@ -XXX,XX +XXX,XX @@ typedef struct SMMUv3AccelState { uint32_t bypass_hwpt_id; uint32_t abort_hwpt_id; QLIST_HEAD(, SMMUv3AccelDevice) device_list; + bool auto_mode; + bool auto_finalised; } SMMUv3AccelState; typedef struct SMMUS1Hwpt { diff --git a/include/hw/arm/smmuv3.h b/include/hw/arm/smmuv3.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/smmuv3.h +++ b/include/hw/arm/smmuv3.h @@ -XXX,XX +XXX,XX @@ #include "hw/arm/smmu-common.h" #include "qom/object.h" #include "qapi/qapi-types-misc-arm.h" +#include "qemu/notify.h" #define TYPE_SMMUV3_IOMMU_MEMORY_REGION "smmuv3-iommu-memory-region" @@ -XXX,XX +XXX,XX @@ struct SMMUv3State { OnOffAuto ats; OasMode oas; SsidSizeMode ssidsize; + + Notifier machine_done; }; typedef enum { -- 2.43.0
From: Nathan Chen <nathanc@nvidia.com> Allow accelerated SMMUv3 Address Translation Services support property to be derived from host IOMMU capabilities. Derive host values using IOMMU_GET_HW_INFO, retrieving ATS capability from IDR0. Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Nathan Chen <nathanc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260608174900.2227340-5-nathanc@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-accel.c | 9 +++++++++ hw/arm/smmuv3.c | 11 ++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -XXX,XX +XXX,XX @@ static void smmuv3_accel_auto_finalise(SMMUv3State *s, return; } + if (s->ats == ON_OFF_AUTO_AUTO) { + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, ATS, + FIELD_EX32(info->idr[0], IDR0, ATS)); + } + accel->auto_finalised = true; } @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_init(SMMUv3State *s, Error **errp) bs->iommu_ops = &smmuv3_accel_ops; smmuv3_accel_as_init(s); + if (s->ats == ON_OFF_AUTO_AUTO) { + s->s_accel->auto_mode = true; + } + if (s->s_accel->auto_mode) { s->machine_done.notify = smmuv3_accel_machine_done; qemu_add_machine_init_done_notifier(&s->machine_done); diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -XXX,XX +XXX,XX @@ static void smmu_reset_exit(Object *obj, ResetType type) static bool smmu_validate_property(SMMUv3State *s, Error **errp) { - if (s->ats == ON_OFF_AUTO_AUTO) { - error_setg(errp, "ats auto mode is not supported"); - return false; - } if (s->ril == ON_OFF_AUTO_AUTO) { error_setg(errp, "ril auto mode is not supported"); return false; @@ -XXX,XX +XXX,XX @@ static void smmuv3_class_init(ObjectClass *klass, const void *data) "Disable range invalidation support (for accel=on). ril=auto " "is not supported."); object_class_property_set_description(klass, "ats", - "Enable/disable ATS support (for accel=on). Please ensure host " - "platform has ATS support before enabling this. ats=auto is not " - "supported."); + "Enable/disable ATS support (for accel=on). " + "Valid values are on, off, and auto. Defaults to off. " + "Please ensure host platform supports ATS before setting it " + "to on."); object_class_property_set_description(klass, "oas", "Specify Output Address Size (for accel=on). Supported values " "are 44 or 48 bits. Defaults to 44 bits. oas=auto is not " -- 2.43.0
From: Nathan Chen <nathanc@nvidia.com> Allow accelerated SMMUv3 Range Invalidation support property to be derived from host IOMMU capabilities. Derive host values using IOMMU_GET_HW_INFO, retrieving RIL capability from IDR3. Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Nathan Chen <nathanc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260608174900.2227340-6-nathanc@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-accel.c | 8 +++++++- hw/arm/smmuv3.c | 10 ++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -XXX,XX +XXX,XX @@ static void smmuv3_accel_auto_finalise(SMMUv3State *s, FIELD_EX32(info->idr[0], IDR0, ATS)); } + if (s->ril == ON_OFF_AUTO_AUTO) { + s->idr[3] = FIELD_DP32(s->idr[3], IDR3, RIL, + FIELD_EX32(info->idr[3], IDR3, RIL)); + } + accel->auto_finalised = true; } @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_init(SMMUv3State *s, Error **errp) bs->iommu_ops = &smmuv3_accel_ops; smmuv3_accel_as_init(s); - if (s->ats == ON_OFF_AUTO_AUTO) { + if (s->ats == ON_OFF_AUTO_AUTO || + s->ril == ON_OFF_AUTO_AUTO) { s->s_accel->auto_mode = true; } diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -XXX,XX +XXX,XX @@ static void smmu_reset_exit(Object *obj, ResetType type) static bool smmu_validate_property(SMMUv3State *s, Error **errp) { - if (s->ril == ON_OFF_AUTO_AUTO) { - error_setg(errp, "ril auto mode is not supported"); - return false; - } if (s->ssidsize == SSID_SIZE_MODE_AUTO) { error_setg(errp, "ssidsize auto mode is not supported"); return false; @@ -XXX,XX +XXX,XX @@ static void smmuv3_class_init(ObjectClass *klass, const void *data) "ensure the host SMMUv3 supports nested translation before " "enabling."); object_class_property_set_description(klass, "ril", - "Disable range invalidation support (for accel=on). ril=auto " - "is not supported."); + "Enable/disable range invalidation support (for accel=on). " + "Valid values are on, off, and auto. Defaults to on. " + "Any attempt to turn it 'on' while the host does not support " + "it would fail."); object_class_property_set_description(klass, "ats", "Enable/disable ATS support (for accel=on). " "Valid values are on, off, and auto. Defaults to off. " -- 2.43.0
From: Nathan Chen <nathanc@nvidia.com> Allow accelerated SMMUv3 SSID size property to be derived from host IOMMU capabilities. Derive host values using IOMMU_GET_HW_INFO, retrieving SSID size from IDR1. When the auto SSID size is resolved to a non-zero value, PASID capability is advertised to the vIOMMU and accelerated use cases such as Shared Virtual Addressing (SVA) are supported. Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Nathan Chen <nathanc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260608174900.2227340-7-nathanc@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-accel.c | 17 +++++++++++++++-- hw/arm/smmuv3.c | 22 ++++++++++++---------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -XXX,XX +XXX,XX @@ static void smmuv3_accel_auto_finalise(SMMUv3State *s, FIELD_EX32(info->idr[3], IDR3, RIL)); } + if (s->ssidsize == SSID_SIZE_MODE_AUTO) { + s->idr[1] = FIELD_DP32(s->idr[1], IDR1, SSIDSIZE, + FIELD_EX32(info->idr[1], IDR1, SSIDSIZE)); + } + accel->auto_finalised = true; } @@ -XXX,XX +XXX,XX @@ static AddressSpace *smmuv3_accel_find_add_as(PCIBus *bus, void *opaque, } } +static inline bool smmuv3_pasid_supported(SMMUv3State *s) +{ + return s->ssidsize > SSID_SIZE_MODE_0 || + (s->ssidsize == SSID_SIZE_MODE_AUTO && + FIELD_EX32(s->idr[1], IDR1, SSIDSIZE)); +} + static uint64_t smmuv3_accel_get_viommu_flags(void *opaque) { /* @@ -XXX,XX +XXX,XX @@ static uint64_t smmuv3_accel_get_viommu_flags(void *opaque) SMMUState *bs = opaque; SMMUv3State *s = ARM_SMMUV3(bs); - if (s->ssidsize > SSID_SIZE_MODE_0) { + if (smmuv3_pasid_supported(s)) { flags |= VIOMMU_FLAG_PASID_SUPPORTED; } return flags; @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_init(SMMUv3State *s, Error **errp) smmuv3_accel_as_init(s); if (s->ats == ON_OFF_AUTO_AUTO || - s->ril == ON_OFF_AUTO_AUTO) { + s->ril == ON_OFF_AUTO_AUTO || + s->ssidsize == SSID_SIZE_MODE_AUTO) { s->s_accel->auto_mode = true; } diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -XXX,XX +XXX,XX @@ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, } /* Multiple context descriptors require SubstreamID support */ - if (s->ssidsize == SSID_SIZE_MODE_0 && STE_S1CDMAX(ste) != 0) { + if ((s->ssidsize == SSID_SIZE_MODE_0 || + (s->ssidsize == SSID_SIZE_MODE_AUTO && + !FIELD_EX32(s->idr[1], IDR1, SSIDSIZE))) && + STE_S1CDMAX(ste) != 0) { qemu_log_mask(LOG_UNIMP, "SMMUv3: multiple S1 context descriptors require SubstreamID support. " "Configure ssidsize > 0 (requires accel=on)\n"); @@ -XXX,XX +XXX,XX @@ static void smmu_reset_exit(Object *obj, ResetType type) static bool smmu_validate_property(SMMUv3State *s, Error **errp) { - if (s->ssidsize == SSID_SIZE_MODE_AUTO) { - error_setg(errp, "ssidsize auto mode is not supported"); - return false; - } if (s->oas != OAS_MODE_44 && s->oas != OAS_MODE_48) { error_setg(errp, "QEMU SMMUv3 model only implements 44 and 48 bit" "OAS; other OasMode values are not supported"); @@ -XXX,XX +XXX,XX @@ static bool smmu_validate_property(SMMUv3State *s, Error **errp) return false; } if (s->ssidsize > SSID_SIZE_MODE_0) { - error_setg(errp, "ssidsize can only be set if accel=on"); + error_setg(errp, "ssidsize can only be greater than 0 " + "bits if accel=on"); return false; } return true; @@ -XXX,XX +XXX,XX @@ static void smmuv3_class_init(ObjectClass *klass, const void *data) "are 44 or 48 bits. Defaults to 44 bits. oas=auto is not " "supported."); object_class_property_set_description(klass, "ssidsize", - "Number of bits used to represent SubstreamIDs (SSIDs). " + "Set number of bits used to represent SubstreamIDs (SSIDs). " + "Valid values are 0-20 and auto. Defaults to 0. " "A value of N allows SSIDs in the range [0 .. 2^N - 1]. " - "Valid range is 0-20, where 0 disables SubstreamID support. " - "Defaults to 0. A value greater than 0 is required to enable " - "PASID support. ssidsize=auto is not supported."); + "A value of 0 disables SubstreamID support. A value greater " + "than 0 is required to enable PASID support." + "Please ensure the value does not exceed the maximum " + "SubstreamID size supported by the host platform."); } static int smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu, -- 2.43.0
From: Nathan Chen <nathanc@nvidia.com> Allow accelerated SMMUv3 OAS property to be derived from host IOMMU capabilities. Derive host values using IOMMU_GET_HW_INFO, retrieving OAS from IDR5. This keeps the OAS value advertised by the virtual SMMU compatible with the capabilities of the host SMMUv3, so that the intermediate physical addresses (IPA) consumed by host SMMU for stage-2 translation do not exceed the host's max supported IPA size. Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Nathan Chen <nathanc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260608174900.2227340-8-nathanc@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-accel.c | 8 +++++++- hw/arm/smmuv3.c | 17 ++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -XXX,XX +XXX,XX @@ static void smmuv3_accel_auto_finalise(SMMUv3State *s, FIELD_EX32(info->idr[1], IDR1, SSIDSIZE)); } + if (s->oas == OAS_MODE_AUTO) { + s->idr[5] = FIELD_DP32(s->idr[5], IDR5, OAS, + FIELD_EX32(info->idr[5], IDR5, OAS)); + } + accel->auto_finalised = true; } @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_init(SMMUv3State *s, Error **errp) if (s->ats == ON_OFF_AUTO_AUTO || s->ril == ON_OFF_AUTO_AUTO || - s->ssidsize == SSID_SIZE_MODE_AUTO) { + s->ssidsize == SSID_SIZE_MODE_AUTO || + s->oas == OAS_MODE_AUTO) { s->s_accel->auto_mode = true; } diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -XXX,XX +XXX,XX @@ static void smmu_reset_exit(Object *obj, ResetType type) static bool smmu_validate_property(SMMUv3State *s, Error **errp) { - if (s->oas != OAS_MODE_44 && s->oas != OAS_MODE_48) { - error_setg(errp, "QEMU SMMUv3 model only implements 44 and 48 bit" - "OAS; other OasMode values are not supported"); + if (s->oas != OAS_MODE_44 && s->oas != OAS_MODE_48 && + s->oas != OAS_MODE_AUTO) { + error_setg(errp, "QEMU SMMUv3 model only implements auto, " + "44 bit, or 48 bit OAS. Other OasMode values are " + "not supported."); return false; } @@ -XXX,XX +XXX,XX @@ static bool smmu_validate_property(SMMUv3State *s, Error **errp) return false; } if (s->oas > OAS_MODE_44) { - error_setg(errp, "OAS must be 44 bits when accel=off"); + error_setg(errp, "oas must be 44 bits when accel=off"); return false; } if (s->ssidsize > SSID_SIZE_MODE_0) { @@ -XXX,XX +XXX,XX @@ static void smmuv3_class_init(ObjectClass *klass, const void *data) "Please ensure host platform supports ATS before setting it " "to on."); object_class_property_set_description(klass, "oas", - "Specify Output Address Size (for accel=on). Supported values " - "are 44 or 48 bits. Defaults to 44 bits. oas=auto is not " - "supported."); + "Set Output Address Size in bits (for accel=on). " + "Valid values are 44, 48, and auto. Defaults to 44 bits." + "Please ensure the value does not exceed the maximum " + "Output Address Size supported by the host platform."); object_class_property_set_description(klass, "ssidsize", "Set number of bits used to represent SubstreamIDs (SSIDs). " "Valid values are 0-20 and auto. Defaults to 0. " -- 2.43.0
From: Nathan Chen <nathanc@nvidia.com> Set the default value of ATS, RIL, SSIDSIZE, and OAS to auto, in order to match the host IOMMU properties when accel=on. If accel=off and these property values are set to auto, the default property values defined in smmuv3_init_id_regs() for OAS and RIL will remain unchanged, while SSIDSIZE and ATS values will remain initialized at 0. Introduce a new compat for the changed defaults. Reviewed-by: Shameer Kolothum <skolothumtho@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Nathan Chen <nathanc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260608174900.2227340-9-nathanc@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3.c | 23 +++++++++++++++-------- hw/core/machine.c | 5 +++++ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -XXX,XX +XXX,XX @@ static const Property smmuv3_properties[] = { DEFINE_PROP_BOOL("accel", SMMUv3State, accel, false), /* GPA of MSI doorbell, for SMMUv3 accel use. */ DEFINE_PROP_UINT64("msi-gpa", SMMUv3State, msi_gpa, 0), + /* + * AUTO values for accel=off will resolve to: + * ril: on + * ats: off + * oas: 44 + * ssidsize: 0 + */ /* RIL can be turned off for accel cases */ - DEFINE_PROP_ON_OFF_AUTO("ril", SMMUv3State, ril, ON_OFF_AUTO_ON), - DEFINE_PROP_ON_OFF_AUTO("ats", SMMUv3State, ats, ON_OFF_AUTO_OFF), - DEFINE_PROP_OAS_MODE("oas", SMMUv3State, oas, OAS_MODE_44), + DEFINE_PROP_ON_OFF_AUTO("ril", SMMUv3State, ril, ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO("ats", SMMUv3State, ats, ON_OFF_AUTO_AUTO), + DEFINE_PROP_OAS_MODE("oas", SMMUv3State, oas, OAS_MODE_AUTO), DEFINE_PROP_SSIDSIZE_MODE("ssidsize", SMMUv3State, ssidsize, - SSID_SIZE_MODE_0), + SSID_SIZE_MODE_AUTO), }; static void smmuv3_instance_init(Object *obj) @@ -XXX,XX +XXX,XX @@ static void smmuv3_class_init(ObjectClass *klass, const void *data) "enabling."); object_class_property_set_description(klass, "ril", "Enable/disable range invalidation support (for accel=on). " - "Valid values are on, off, and auto. Defaults to on. " + "Valid values are on, off, and auto. Defaults to auto. " "Any attempt to turn it 'on' while the host does not support " "it would fail."); object_class_property_set_description(klass, "ats", "Enable/disable ATS support (for accel=on). " - "Valid values are on, off, and auto. Defaults to off. " + "Valid values are on, off, and auto. Defaults to auto. " "Please ensure host platform supports ATS before setting it " "to on."); object_class_property_set_description(klass, "oas", "Set Output Address Size in bits (for accel=on). " - "Valid values are 44, 48, and auto. Defaults to 44 bits." + "Valid values are 44, 48, and auto. Defaults to auto." "Please ensure the value does not exceed the maximum " "Output Address Size supported by the host platform."); object_class_property_set_description(klass, "ssidsize", "Set number of bits used to represent SubstreamIDs (SSIDs). " - "Valid values are 0-20 and auto. Defaults to 0. " + "Valid values are 0-20 and auto. Defaults to auto. " "A value of N allows SSIDs in the range [0 .. 2^N - 1]. " "A value of 0 disables SubstreamID support. A value greater " "than 0 is required to enable PASID support." diff --git a/hw/core/machine.c b/hw/core/machine.c index XXXXXXX..XXXXXXX 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -XXX,XX +XXX,XX @@ #include "hw/virtio/virtio-iommu.h" #include "hw/acpi/generic_event_device.h" #include "qemu/audio.h" +#include "hw/arm/smmuv3.h" GlobalProperty hw_compat_11_0[] = { { "chardev-vc", "encoding", "cp437" }, { "tpm-crb", "cap-chunk", "off" }, { "tpm-crb", "x-allow-chunk-migration", "off" }, + { TYPE_ARM_SMMUV3, "ats", "off" }, + { TYPE_ARM_SMMUV3, "ril", "on" }, + { TYPE_ARM_SMMUV3, "ssidsize", "0" }, + { TYPE_ARM_SMMUV3, "oas", "44" }, }; const size_t hw_compat_11_0_len = G_N_ELEMENTS(hw_compat_11_0); -- 2.43.0
From: Nathan Chen <nathanc@nvidia.com> Update documentation now that "auto" is supported for accelerated SMMUv3 properties. Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Nathan Chen <nathanc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260608174900.2227340-10-nathanc@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- qemu-options.hx | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/qemu-options.hx b/qemu-options.hx index XXXXXXX..XXXXXXX 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -XXX,XX +XXX,XX @@ SRST Enabling accel configures the host SMMUv3 in nested mode to support vfio-pci passthrough. - The following options are available when accel=on. - Note: 'auto' mode is not currently supported. + The following options will be set to auto by default if not manually + set. When accel=on and these properties are set to auto, the value is + derived from the host SMMUv3 capabilities via IOMMU_GET_HW_INFO. With + accel=on, this requires at least one cold-plugged vfio-pci device; if + none is present at machine init, QEMU will abort. - ``ril=on|off`` (default: on) + If accel=off, auto values resolve to the non-accel defaults given below. + + ``ril=on|off|auto`` (default: auto) Support for Range Invalidation, which allows the SMMUv3 driver to invalidate TLB entries for a range of IOVAs at once instead of issuing separate commands to invalidate each page. Must match with host SMMUv3 Range Invalidation support. - ``ats=on|off`` (default: off) + - With accel=off, auto is resolved to 'on'. + + ``ats=on|off|auto`` (default: auto) Support for Address Translation Services, which enables PCIe devices to cache address translations in their local TLB and reduce latency. Host SMMUv3 must support ATS in order to enable this feature for the vIOMMU. - ``oas=val`` (supported values are 44 and 48. default: 44) + - With accel=off, auto is resolved to 'off'. + + ``oas=val|auto`` (supported values are 44 and 48. default: auto) Sets the Output Address Size in bits. The value set here must be less than or equal to the host SMMUv3's supported OAS, so that the intermediate physical addresses (IPA) consumed by host SMMU for stage-2 translation do not exceed the host's max supported IPA size. - ``ssidsize=val`` (val between 0 and 20. default: 0) + - With accel=off, auto is resolved to 44. + + ``ssidsize=val|auto`` (val between 0 and 20. default: auto) Sets the Substream ID size in bits. When set to a non-zero value, PASID capability is advertised to the vIOMMU and accelerated use cases such as Shared Virtual Addressing (SVA) are supported. + - With accel=off, auto is resolved to 0. + ``-device amd-iommu[,option=...]`` Enables emulation of an AMD-Vi I/O Memory Management Unit (IOMMU). Only available with ``-machine q35``, it supports the following options: -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> Currently it is possible to attach several arm-smmuv3 devices to the same bus although it is a wrong setup. Change the prototype of pci_setup_iommu_per_bus to pass an error handle. This latter is set when iommu_per_bus is already set and used by the single caller (smmu_base_realize) to report a useful error to the end-user. While at it document pci_setup_iommu_per_bus callback in the header. Fixes: 66d2f665e163 ("hw/arm/virt: Allow user-creatable SMMUv3 dev instantiation") Signed-off-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nathan Chen <nathanc@nvidia.com> Reviewed-by: Shameer Kolothum <skolothumtho@nvidia.com> Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@mailo.com> Message-id: 20260603124415.1120808-1-eric.auger@redhat.com Acked-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmu-common.c | 4 +++- hw/pci/pci.c | 9 +++++++-- include/hw/pci/pci.h | 16 +++++++++++++++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -XXX,XX +XXX,XX @@ static void smmu_base_realize(DeviceState *dev, Error **errp) } if (s->smmu_per_bus) { - pci_setup_iommu_per_bus(pci_bus, s->iommu_ops, s); + if (!pci_setup_iommu_per_bus(pci_bus, s->iommu_ops, s, errp)) { + return; + } } else { pci_setup_iommu(pci_bus, s->iommu_ops, s); } diff --git a/hw/pci/pci.c b/hw/pci/pci.c index XXXXXXX..XXXXXXX 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -XXX,XX +XXX,XX @@ void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque) * IOMMU ops are returned, avoiding the use of the parent’s IOMMU when * it's not appropriate. */ -void pci_setup_iommu_per_bus(PCIBus *bus, const PCIIOMMUOps *ops, - void *opaque) +bool pci_setup_iommu_per_bus(PCIBus *bus, const PCIIOMMUOps *ops, + void *opaque, Error **errp) { + if (bus->iommu_per_bus) { + error_setg(errp, "An iommu is already attached to this bus"); + return false; + } pci_setup_iommu(bus, ops, opaque); bus->iommu_per_bus = true; + return true; } static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -XXX,XX +XXX,XX @@ int pci_iommu_unregister_iotlb_notifier(PCIDevice *dev, uint32_t pasid, */ void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque); -void pci_setup_iommu_per_bus(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque); +/** + * pci_setup_iommu_per_bus: Initialize specific IOMMU handlers for a PCIBus + * + * Similar to pci_setup_iommu but enforces that the iommu only protects + * @bus downstream end points and no other bus hierarchy + * + * @bus: the #PCIBus being updated. + * @ops: the #PCIIOMMUOps + * @opaque: passed to callbacks of the @ops structure. + * @errp: error handle + * + * Returns false on failure with @errp set, true on success + */ +bool pci_setup_iommu_per_bus(PCIBus *bus, const PCIIOMMUOps *ops, + void *opaque, Error **errp); pcibus_t pci_bar_address(PCIDevice *d, int reg, uint8_t type, pcibus_t size); -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> The updated IOMMUFD uAPI introduces the ability for userspace to request a specific hardware info data type via IOMMU_GET_HW_INFO. Update iommufd_backend_get_device_info() to set IOMMU_HW_INFO_FLAG_INPUT_TYPE when a non-zero type is supplied, and adjust all callers to pass a type value explicitly initialised to zero (IOMMU_HW_INFO_TYPE_DEFAULT) when no specific type is requested. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-2-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- backends/iommufd.c | 7 +++++++ hw/arm/smmuv3-accel.c | 2 +- hw/vfio/iommufd.c | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/backends/iommufd.c b/backends/iommufd.c index XXXXXXX..XXXXXXX 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -XXX,XX +XXX,XX @@ bool iommufd_backend_get_dirty_bitmap(IOMMUFDBackend *be, return true; } +/* + * @type can carry a desired HW info type defined in the uapi headers. If caller + * doesn't have one, indicating it wants the default type, then @type should be + * zeroed (i.e. IOMMU_HW_INFO_TYPE_DEFAULT). + */ bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid, uint32_t *type, void *data, uint32_t len, uint64_t *caps, uint8_t *max_pasid_log2, Error **errp) { struct iommu_hw_info info = { + .flags = (*type) ? IOMMU_HW_INFO_FLAG_INPUT_TYPE : 0, .size = sizeof(info), .dev_id = devid, .data_len = len, .data_uptr = (uintptr_t)data, + .in_data_type = *type, }; if (ioctl(be->fd, IOMMU_GET_HW_INFO, &info)) { diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -XXX,XX +XXX,XX @@ smmuv3_accel_hw_compatible(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *hiodi, Error **errp) { struct iommu_hw_info_arm_smmuv3 info; - uint32_t data_type; + uint32_t data_type = IOMMU_HW_INFO_TYPE_DEFAULT; uint64_t caps; if (!iommufd_backend_get_device_info(hiodi->iommufd, hiodi->devid, diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index XXXXXXX..XXXXXXX 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -XXX,XX +XXX,XX @@ static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, IOMMUFDBackend *iommufd = vbasedev->iommufd; VFIOContainer *bcontainer = VFIO_IOMMU(container); bool viommu_nesting, viommu_nesting_dirty; - uint32_t type, flags = 0; + uint32_t type = IOMMU_HW_INFO_TYPE_DEFAULT, flags = 0; uint64_t hw_caps; VendorCaps caps; VFIOIOASHwpt *hwpt; @@ -XXX,XX +XXX,XX @@ static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, HostIOMMUDeviceIOMMUFD *hiodi; HostIOMMUDeviceCaps *caps = &hiod->caps; VendorCaps *vendor_caps = &caps->vendor_caps; - enum iommu_hw_info_type type; + uint32_t type = IOMMU_HW_INFO_TYPE_DEFAULT; uint8_t max_pasid_log2; uint64_t hw_caps; -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> The updated IOMMUFD VIOMMU_ALLOC uAPI allows userspace to provide a data buffer when creating a vIOMMU (e.g. for Tegra241 CMDQV). Extend iommufd_backend_alloc_viommu() to pass a user pointer and size to the kernel. Update the caller accordingly. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-3-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- backends/iommufd.c | 4 ++++ backends/trace-events | 2 +- hw/arm/smmuv3-accel.c | 4 ++-- include/system/iommufd.h | 1 + 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/backends/iommufd.c b/backends/iommufd.c index XXXXXXX..XXXXXXX 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -XXX,XX +XXX,XX @@ bool iommufd_backend_invalidate_cache(IOMMUFDBackend *be, uint32_t id, bool iommufd_backend_alloc_viommu(IOMMUFDBackend *be, uint32_t dev_id, uint32_t viommu_type, uint32_t hwpt_id, + void *data_ptr, uint32_t data_len, uint32_t *out_viommu_id, Error **errp) { int ret; @@ -XXX,XX +XXX,XX @@ bool iommufd_backend_alloc_viommu(IOMMUFDBackend *be, uint32_t dev_id, .type = viommu_type, .dev_id = dev_id, .hwpt_id = hwpt_id, + .data_len = data_len, + .data_uptr = (uintptr_t)data_ptr, }; ret = ioctl(be->fd, IOMMU_VIOMMU_ALLOC, &alloc_viommu); trace_iommufd_backend_alloc_viommu(be->fd, dev_id, viommu_type, hwpt_id, + (uintptr_t)data_ptr, data_len, alloc_viommu.out_viommu_id, ret); if (ret) { error_setg_errno(errp, errno, "IOMMU_VIOMMU_ALLOC failed"); diff --git a/backends/trace-events b/backends/trace-events index XXXXXXX..XXXXXXX 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -XXX,XX +XXX,XX @@ iommufd_backend_free_id(int iommufd, uint32_t id, int ret) " iommufd=%d id=%d (% iommufd_backend_set_dirty(int iommufd, uint32_t hwpt_id, bool start, int ret) " iommufd=%d hwpt=%u enable=%d (%d)" iommufd_backend_get_dirty_bitmap(int iommufd, uint32_t hwpt_id, uint64_t iova, uint64_t size, uint64_t flags, uint64_t page_size, int ret) " iommufd=%d hwpt=%u iova=0x%"PRIx64" size=0x%"PRIx64" flags=0x%"PRIx64" page_size=0x%"PRIx64" (%d)" iommufd_backend_invalidate_cache(int iommufd, uint32_t id, uint32_t data_type, uint32_t entry_len, uint32_t entry_num, uint32_t done_num, uint64_t data_ptr, int ret) " iommufd=%d id=%u data_type=%u entry_len=%u entry_num=%u done_num=%u data_ptr=0x%"PRIx64" (%d)" -iommufd_backend_alloc_viommu(int iommufd, uint32_t dev_id, uint32_t type, uint32_t hwpt_id, uint32_t viommu_id, int ret) " iommufd=%d type=%u dev_id=%u hwpt_id=%u viommu_id=%u (%d)" +iommufd_backend_alloc_viommu(int iommufd, uint32_t dev_id, uint32_t type, uint32_t hwpt_id, uint64_t data_ptr, uint32_t data_len, uint32_t viommu_id, int ret) " iommufd=%d type=%u dev_id=%u hwpt_id=%u data_ptr=0x%"PRIx64" data_len=0x%x viommu_id=%u (%d)" iommufd_backend_alloc_vdev(int iommufd, uint32_t dev_id, uint32_t viommu_id, uint64_t virt_id, uint32_t vdev_id, int ret) " iommufd=%d dev_id=%u viommu_id=%u virt_id=0x%"PRIx64" vdev_id=%u (%d)" iommufd_viommu_alloc_eventq(int iommufd, uint32_t viommu_id, uint32_t type, uint32_t veventq_id, uint32_t veventq_fd, int ret) " iommufd=%d viommu_id=%u type=%u veventq_id=%u veventq_fd=%u (%d)" diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -XXX,XX +XXX,XX @@ smmuv3_accel_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *hiodi, IOMMUFDViommu *viommu; if (!iommufd_backend_alloc_viommu(hiodi->iommufd, hiodi->devid, - IOMMU_VIOMMU_TYPE_ARM_SMMUV3, - s2_hwpt_id, &viommu_id, errp)) { + IOMMU_VIOMMU_TYPE_ARM_SMMUV3, s2_hwpt_id, + NULL, 0, &viommu_id, errp)) { return false; } diff --git a/include/system/iommufd.h b/include/system/iommufd.h index XXXXXXX..XXXXXXX 100644 --- a/include/system/iommufd.h +++ b/include/system/iommufd.h @@ -XXX,XX +XXX,XX @@ bool iommufd_backend_alloc_hwpt(IOMMUFDBackend *be, uint32_t dev_id, Error **errp); bool iommufd_backend_alloc_viommu(IOMMUFDBackend *be, uint32_t dev_id, uint32_t viommu_type, uint32_t hwpt_id, + void *data_ptr, uint32_t data_len, uint32_t *out_hwpt, Error **errp); bool iommufd_backend_alloc_vdev(IOMMUFDBackend *be, uint32_t dev_id, -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> Add a helper to allocate an iommufd backed HW queue for a vIOMMU. While at it, define a struct IOMMUFDHWqueue for use by vendor implementations. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-4-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- backends/iommufd.c | 31 +++++++++++++++++++++++++++++++ backends/trace-events | 1 + include/system/iommufd.h | 11 +++++++++++ 3 files changed, 43 insertions(+) diff --git a/backends/iommufd.c b/backends/iommufd.c index XXXXXXX..XXXXXXX 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -XXX,XX +XXX,XX @@ bool iommufd_backend_alloc_veventq(IOMMUFDBackend *be, uint32_t viommu_id, return true; } +bool iommufd_backend_alloc_hw_queue(IOMMUFDBackend *be, uint32_t viommu_id, + uint32_t queue_type, uint32_t index, + uint64_t addr, uint64_t length, + uint32_t *out_hw_queue_id, Error **errp) +{ + int ret; + struct iommu_hw_queue_alloc alloc_hw_queue = { + .size = sizeof(alloc_hw_queue), + .flags = 0, + .viommu_id = viommu_id, + .type = queue_type, + .index = index, + .nesting_parent_iova = addr, + .length = length, + }; + + ret = ioctl(be->fd, IOMMU_HW_QUEUE_ALLOC, &alloc_hw_queue); + + trace_iommufd_backend_alloc_hw_queue(be->fd, viommu_id, queue_type, + index, addr, length, + alloc_hw_queue.out_hw_queue_id, ret); + if (ret) { + error_setg_errno(errp, errno, "IOMMU_HW_QUEUE_ALLOC failed"); + return false; + } + + g_assert(out_hw_queue_id); + *out_hw_queue_id = alloc_hw_queue.out_hw_queue_id; + return true; +} + bool host_iommu_device_iommufd_attach_hwpt(HostIOMMUDeviceIOMMUFD *hiodi, uint32_t hwpt_id, Error **errp) { diff --git a/backends/trace-events b/backends/trace-events index XXXXXXX..XXXXXXX 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -XXX,XX +XXX,XX @@ iommufd_backend_invalidate_cache(int iommufd, uint32_t id, uint32_t data_type, u iommufd_backend_alloc_viommu(int iommufd, uint32_t dev_id, uint32_t type, uint32_t hwpt_id, uint64_t data_ptr, uint32_t data_len, uint32_t viommu_id, int ret) " iommufd=%d type=%u dev_id=%u hwpt_id=%u data_ptr=0x%"PRIx64" data_len=0x%x viommu_id=%u (%d)" iommufd_backend_alloc_vdev(int iommufd, uint32_t dev_id, uint32_t viommu_id, uint64_t virt_id, uint32_t vdev_id, int ret) " iommufd=%d dev_id=%u viommu_id=%u virt_id=0x%"PRIx64" vdev_id=%u (%d)" iommufd_viommu_alloc_eventq(int iommufd, uint32_t viommu_id, uint32_t type, uint32_t veventq_id, uint32_t veventq_fd, int ret) " iommufd=%d viommu_id=%u type=%u veventq_id=%u veventq_fd=%u (%d)" +iommufd_backend_alloc_hw_queue(int iommufd, uint32_t viommu_id, uint32_t queue_type, uint32_t index, uint64_t addr, uint64_t size, uint32_t queue_id, int ret) " iommufd=%d viommu_id=%u queue_type=%u index=%u addr=0x%"PRIx64" size=0x%"PRIx64" queue_id=%u (%d)" # igvm-cfg.c igvm_reset_enter(int type) "type=%u" diff --git a/include/system/iommufd.h b/include/system/iommufd.h index XXXXXXX..XXXXXXX 100644 --- a/include/system/iommufd.h +++ b/include/system/iommufd.h @@ -XXX,XX +XXX,XX @@ typedef struct IOMMUFDVeventq { bool event_start; /* True after first valid event; cleared on overflow */ } IOMMUFDVeventq; +/* HW queue object for a vIOMMU-specific HW-accelerated queue */ +typedef struct IOMMUFDHWqueue { + IOMMUFDViommu *viommu; + uint32_t hw_queue_id; +} IOMMUFDHWqueue; + bool iommufd_backend_connect(IOMMUFDBackend *be, Error **errp); void iommufd_backend_disconnect(IOMMUFDBackend *be); @@ -XXX,XX +XXX,XX @@ bool iommufd_backend_alloc_veventq(IOMMUFDBackend *be, uint32_t viommu_id, uint32_t *out_veventq_id, uint32_t *out_veventq_fd, Error **errp); +bool iommufd_backend_alloc_hw_queue(IOMMUFDBackend *be, uint32_t viommu_id, + uint32_t queue_type, uint32_t index, + uint64_t addr, uint64_t length, + uint32_t *out_hw_queue_id, Error **errp); + bool iommufd_backend_set_dirty_tracking(IOMMUFDBackend *be, uint32_t hwpt_id, bool start, Error **errp); bool iommufd_backend_get_dirty_bitmap(IOMMUFDBackend *be, uint32_t hwpt_id, -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> Add a backend helper to mmap hardware MMIO regions exposed via iommufd for a vIOMMU instance. This allows user space to access HW-accelerated MMIO pages provided by the vIOMMU. The caller is responsible for unmapping the returned region. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-5-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- backends/iommufd.c | 22 ++++++++++++++++++++++ backends/trace-events | 1 + include/system/iommufd.h | 4 ++++ 3 files changed, 27 insertions(+) diff --git a/backends/iommufd.c b/backends/iommufd.c index XXXXXXX..XXXXXXX 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -XXX,XX +XXX,XX @@ bool iommufd_backend_alloc_hw_queue(IOMMUFDBackend *be, uint32_t viommu_id, return true; } +/* + * Helper to mmap HW MMIO regions exposed via iommufd for a vIOMMU instance. + * The caller is responsible for unmapping the mapped region. + */ +bool iommufd_backend_viommu_mmap(IOMMUFDBackend *be, uint32_t viommu_id, + uint64_t size, off_t offset, void **out_ptr, + Error **errp) +{ + g_assert(viommu_id); + g_assert(out_ptr); + + *out_ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, be->fd, + offset); + trace_iommufd_backend_viommu_mmap(be->fd, viommu_id, size, offset); + if (*out_ptr == MAP_FAILED) { + error_setg_errno(errp, errno, "IOMMUFD vIOMMU mmap failed"); + return false; + } + + return true; +} + bool host_iommu_device_iommufd_attach_hwpt(HostIOMMUDeviceIOMMUFD *hiodi, uint32_t hwpt_id, Error **errp) { diff --git a/backends/trace-events b/backends/trace-events index XXXXXXX..XXXXXXX 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -XXX,XX +XXX,XX @@ iommufd_backend_alloc_viommu(int iommufd, uint32_t dev_id, uint32_t type, uint32 iommufd_backend_alloc_vdev(int iommufd, uint32_t dev_id, uint32_t viommu_id, uint64_t virt_id, uint32_t vdev_id, int ret) " iommufd=%d dev_id=%u viommu_id=%u virt_id=0x%"PRIx64" vdev_id=%u (%d)" iommufd_viommu_alloc_eventq(int iommufd, uint32_t viommu_id, uint32_t type, uint32_t veventq_id, uint32_t veventq_fd, int ret) " iommufd=%d viommu_id=%u type=%u veventq_id=%u veventq_fd=%u (%d)" iommufd_backend_alloc_hw_queue(int iommufd, uint32_t viommu_id, uint32_t queue_type, uint32_t index, uint64_t addr, uint64_t size, uint32_t queue_id, int ret) " iommufd=%d viommu_id=%u queue_type=%u index=%u addr=0x%"PRIx64" size=0x%"PRIx64" queue_id=%u (%d)" +iommufd_backend_viommu_mmap(int iommufd, uint32_t viommu_id, uint64_t size, uint64_t offset) " iommufd=%d viommu_id=%u size=0x%"PRIx64" offset=0x%"PRIx64 # igvm-cfg.c igvm_reset_enter(int type) "type=%u" diff --git a/include/system/iommufd.h b/include/system/iommufd.h index XXXXXXX..XXXXXXX 100644 --- a/include/system/iommufd.h +++ b/include/system/iommufd.h @@ -XXX,XX +XXX,XX @@ bool iommufd_backend_alloc_hw_queue(IOMMUFDBackend *be, uint32_t viommu_id, uint64_t addr, uint64_t length, uint32_t *out_hw_queue_id, Error **errp); +bool iommufd_backend_viommu_mmap(IOMMUFDBackend *be, uint32_t viommu_id, + uint64_t size, off_t offset, void **out_ptr, + Error **errp); + bool iommufd_backend_set_dirty_tracking(IOMMUFDBackend *be, uint32_t hwpt_id, bool start, Error **errp); bool iommufd_backend_get_dirty_bitmap(IOMMUFDBackend *be, uint32_t hwpt_id, -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> The viommu field is assigned but never used. Callers freeing the veventq already have access to the IOMMUFDViommu object through other references, so this field is redundant. Removing it also simplifies upcoming changes where veventq is allocated based on the viommu id before the IOMMUFDViommu object is created (e.g. vendor CMDQV-based veventq allocation). No functional change. Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-6-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-accel.c | 1 - include/system/iommufd.h | 1 - 2 files changed, 2 deletions(-) diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_alloc_veventq(SMMUv3State *s, Error **errp) veventq = g_new0(IOMMUFDVeventq, 1); veventq->veventq_id = veventq_id; veventq->veventq_fd = veventq_fd; - veventq->viommu = accel->viommu; accel->veventq = veventq; /* Set up event handler for veventq fd */ diff --git a/include/system/iommufd.h b/include/system/iommufd.h index XXXXXXX..XXXXXXX 100644 --- a/include/system/iommufd.h +++ b/include/system/iommufd.h @@ -XXX,XX +XXX,XX @@ typedef struct IOMMUFDVdev { /* Virtual event queue interface for a vIOMMU */ typedef struct IOMMUFDVeventq { - IOMMUFDViommu *viommu; uint32_t veventq_id; uint32_t veventq_fd; uint32_t last_event_seq; /* Sequence number of last processed event */ -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> Command Queue Virtualization (CMDQV) is a hardware extension available on certain platforms that allows the SMMUv3 command queue to be virtualized and passed through to a VM, improving performance. For example, NVIDIA Tegra241 implements CMDQV to support virtualization of multiple command queues (VCMDQs). The term CMDQV is used here generically to refer to any platform that provides hardware support to virtualize the SMMUv3 command queue. CMDQV support is a specialization of the IOMMUFD-backed accelerated SMMUv3 path. Introduce an ops interface to factor out CMDQV-specific probe, initialization, and vIOMMU allocation logic from the base implementation. The ops pointer and associated state are stored in the accelerated SMMUv3 state. This provides an extensible design to support future vendor-specific CMDQV implementations. No functional change. Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-7-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-accel.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/hw/arm/smmuv3-accel.h b/hw/arm/smmuv3-accel.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.h +++ b/hw/arm/smmuv3-accel.h @@ -XXX,XX +XXX,XX @@ #define HW_ARM_SMMUV3_ACCEL_H #include "hw/arm/smmu-common.h" +#include "hw/arm/smmuv3.h" #include "system/iommufd.h" #ifdef CONFIG_LINUX #include <linux/iommufd.h> #endif +/* + * CMDQ-Virtualization (CMDQV) hardware support, extends the SMMUv3 to + * support multiple VCMDQs with virtualization capabilities. + * CMDQV specific behavior is factored behind this ops interface. + */ +typedef struct SMMUv3AccelCmdqvOps { + /** + * @probe: Mandatory. Vendor-specific device probing. + */ + bool (*probe)(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, Error **errp); + /** + * @init: Optional callback. Initialize CMDQV hardware. + */ + bool (*init)(SMMUv3State *s, Error **errp); + /** + * @alloc_viommu: Mandatory. Allocate CMDQV viommu resources. + */ + bool (*alloc_viommu)(SMMUv3State *s, + HostIOMMUDeviceIOMMUFD *idev, + uint32_t *out_viommu_id, + Error **errp); + /** + * @free_viommu: Optional callback. Free CMDQV viommu resources. + * If NULL, the viommu_id is freed directly via iommufd_backend_free_id(). + */ + void (*free_viommu)(SMMUv3State *s); + /** + * @reset: Optional callback. Reset CMDQV state. + */ + void (*reset)(SMMUv3State *s); +} SMMUv3AccelCmdqvOps; + /* * Represents an accelerated SMMU instance backed by an iommufd vIOMMU object. * Holds bypass and abort proxy HWPT IDs used for device attachment. @@ -XXX,XX +XXX,XX @@ typedef struct SMMUv3AccelState { QLIST_HEAD(, SMMUv3AccelDevice) device_list; bool auto_mode; bool auto_finalised; + const SMMUv3AccelCmdqvOps *cmdqv_ops; } SMMUv3AccelState; typedef struct SMMUS1Hwpt { -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> Introduce a Tegra241 CMDQV backend that plugs into the SMMUv3 accelerated CMDQV ops interface. This patch wires up the Tegra241 CMDQV backend and provides a stub implementation for CMDQV probe, initialization, vIOMMU allocation and reset handling. Functional CMDQV support is added in follow-up patches. Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-8-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 5 ++++ hw/arm/meson.build | 2 ++ hw/arm/tegra241-cmdqv-stubs.c | 16 ++++++++++ hw/arm/tegra241-cmdqv.c | 56 +++++++++++++++++++++++++++++++++++ hw/arm/tegra241-cmdqv.h | 15 ++++++++++ 5 files changed, 94 insertions(+) create mode 100644 hw/arm/tegra241-cmdqv-stubs.c create mode 100644 hw/arm/tegra241-cmdqv.c create mode 100644 hw/arm/tegra241-cmdqv.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MM_EVK depends on TCG select FSL_IMX8MM +config TEGRA241_CMDQV + bool + depends on ARM_SMMUV3_ACCEL + config ARM_SMMUV3_ACCEL bool depends on ARM_SMMUV3 @@ -XXX,XX +XXX,XX @@ config ARM_SMMUV3_ACCEL config ARM_SMMUV3 bool select ARM_SMMUV3_ACCEL if IOMMUFD + imply TEGRA241_CMDQV config FSL_IMX6UL bool diff --git a/hw/arm/meson.build b/hw/arm/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -XXX,XX +XXX,XX @@ arm_common_ss.add(when: 'CONFIG_FSL_IMX8MM_EVK', if_true: files('imx8mm-evk.c')) arm_common_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c')) arm_common_ss.add(when: 'CONFIG_ARM_SMMUV3_ACCEL', if_true: files('smmuv3-accel.c')) stub_ss.add(files('smmuv3-accel-stubs.c')) +arm_common_ss.add(when: 'CONFIG_TEGRA241_CMDQV', if_true: files('tegra241-cmdqv.c')) +stub_ss.add(files('tegra241-cmdqv-stubs.c')) arm_common_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c')) arm_common_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c')) arm_common_ss.add(when: 'CONFIG_XEN', if_true: files( diff --git a/hw/arm/tegra241-cmdqv-stubs.c b/hw/arm/tegra241-cmdqv-stubs.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/arm/tegra241-cmdqv-stubs.c @@ -XXX,XX +XXX,XX @@ +/* + * Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved + * + * Stubs for Tegra241 CMDQ-Virtualization extension for SMMUv3 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "smmuv3-accel.h" +#include "hw/arm/tegra241-cmdqv.h" + +const SMMUv3AccelCmdqvOps *tegra241_cmdqv_get_ops(void) +{ + return NULL; +} diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/arm/tegra241-cmdqv.c @@ -XXX,XX +XXX,XX @@ +/* + * Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved + * NVIDIA Tegra241 CMDQ-Virtualization extension for SMMUv3 + * + * Written by Nicolin Chen, Shameer Kolothum + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" + +#include "hw/arm/smmuv3.h" +#include "smmuv3-accel.h" +#include "tegra241-cmdqv.h" + +static void tegra241_cmdqv_free_viommu(SMMUv3State *s) +{ +} + +static bool +tegra241_cmdqv_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, + uint32_t *out_viommu_id, Error **errp) +{ + error_setg(errp, "NVIDIA Tegra241 CMDQV is unsupported"); + return false; +} + +static void tegra241_cmdqv_reset(SMMUv3State *s) +{ +} + +static bool tegra241_cmdqv_init(SMMUv3State *s, Error **errp) +{ + error_setg(errp, "NVIDIA Tegra241 CMDQV is unsupported"); + return false; +} + +static bool tegra241_cmdqv_probe(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, + Error **errp) +{ + error_setg(errp, "NVIDIA Tegra241 CMDQV is unsupported"); + return false; +} + +static const SMMUv3AccelCmdqvOps tegra241_cmdqv_ops = { + .probe = tegra241_cmdqv_probe, + .init = tegra241_cmdqv_init, + .alloc_viommu = tegra241_cmdqv_alloc_viommu, + .free_viommu = tegra241_cmdqv_free_viommu, + .reset = tegra241_cmdqv_reset, +}; + +const SMMUv3AccelCmdqvOps *tegra241_cmdqv_get_ops(void) +{ + return &tegra241_cmdqv_ops; +} diff --git a/hw/arm/tegra241-cmdqv.h b/hw/arm/tegra241-cmdqv.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/arm/tegra241-cmdqv.h @@ -XXX,XX +XXX,XX @@ +/* + * Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved + * NVIDIA Tegra241 CMDQ-Virtualization extension for SMMUv3 + * + * Written by Nicolin Chen, Shameer Kolothum + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_ARM_TEGRA241_CMDQV_H +#define HW_ARM_TEGRA241_CMDQV_H + +const SMMUv3AccelCmdqvOps *tegra241_cmdqv_get_ops(void); + +#endif /* HW_ARM_TEGRA241_CMDQV_H */ -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> Add support for selecting and initializing a CMDQV backend based on the cmdqv OnOffAuto property. If set to OFF, CMDQV is not used and the default IOMMUFD-backed allocation path is taken. If set to AUTO, QEMU attempts to probe a CMDQV backend during device setup. If probing succeeds, the selected ops are stored in the accelerated SMMUv3 state and used. If probing fails, QEMU silently falls back to the default path. If set to ON, QEMU requires CMDQV support. Probing is performed during setup and failure results in an error. When a CMDQV backend is active, its callbacks are used for vIOMMU allocation, free, and reset handling. Otherwise, the base implementation is used. The current implementation wires up the Tegra241 CMDQV backend through the generic ops interface. Functional CMDQV behaviour is added in subsequent patches. No functional change. Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-9-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-accel.c | 94 ++++++++++++++++++++++++++++++++++++++--- include/hw/arm/smmuv3.h | 2 + 2 files changed, 89 insertions(+), 7 deletions(-) diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -XXX,XX +XXX,XX @@ #include "smmuv3-internal.h" #include "smmuv3-accel.h" #include "system/system.h" +#include "tegra241-cmdqv.h" /* * The root region aliases the global system memory, and shared_as_sysmem @@ -XXX,XX +XXX,XX @@ smmuv3_accel_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *hiodi, Error **errp) { SMMUv3AccelState *accel = s->s_accel; + const SMMUv3AccelCmdqvOps *cmdqv_ops = accel->cmdqv_ops; struct iommu_hwpt_arm_smmuv3 bypass_data = { .ste = { SMMU_STE_CFG_BYPASS | SMMU_STE_VALID, 0x0ULL }, }; @@ -XXX,XX +XXX,XX @@ smmuv3_accel_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *hiodi, uint32_t viommu_id, hwpt_id; IOMMUFDViommu *viommu; - if (!iommufd_backend_alloc_viommu(hiodi->iommufd, hiodi->devid, - IOMMU_VIOMMU_TYPE_ARM_SMMUV3, s2_hwpt_id, - NULL, 0, &viommu_id, errp)) { - return false; + if (cmdqv_ops) { + if (!cmdqv_ops->alloc_viommu(s, hiodi, &viommu_id, errp)) { + return false; + } + } else { + if (!iommufd_backend_alloc_viommu(hiodi->iommufd, hiodi->devid, + IOMMU_VIOMMU_TYPE_ARM_SMMUV3, + s2_hwpt_id, NULL, 0, &viommu_id, + errp)) { + return false; + } } viommu = g_new0(IOMMUFDViommu, 1); @@ -XXX,XX +XXX,XX @@ free_bypass_hwpt: free_abort_hwpt: iommufd_backend_free_id(hiodi->iommufd, accel->abort_hwpt_id); free_viommu: - iommufd_backend_free_id(hiodi->iommufd, viommu->viommu_id); + if (cmdqv_ops && cmdqv_ops->free_viommu) { + cmdqv_ops->free_viommu(s); + } else { + iommufd_backend_free_id(hiodi->iommufd, viommu->viommu_id); + } g_free(viommu); accel->viommu = NULL; return false; } +static const SMMUv3AccelCmdqvOps * +smmuv3_accel_probe_cmdqv(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, + Error **errp) +{ + const SMMUv3AccelCmdqvOps *ops = tegra241_cmdqv_get_ops(); + + if (!ops) { + error_setg(errp, "No CMDQV ops found"); + return NULL; + } + g_assert(ops->probe); + g_assert(ops->alloc_viommu); + + if (!ops->probe(s, idev, errp)) { + return NULL; + } + return ops; +} + +static bool +smmuv3_accel_select_cmdqv(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, + Error **errp) +{ + const SMMUv3AccelCmdqvOps *ops = NULL; + + if (s->s_accel->cmdqv_ops) { + return true; + } + + switch (s->cmdqv) { + case ON_OFF_AUTO_OFF: + s->s_accel->cmdqv_ops = NULL; + return true; + case ON_OFF_AUTO_AUTO: + ops = smmuv3_accel_probe_cmdqv(s, idev, NULL); + break; + case ON_OFF_AUTO_ON: + ops = smmuv3_accel_probe_cmdqv(s, idev, errp); + if (!ops) { + error_append_hint(errp, "CMDQV requested but not supported"); + return false; + } + break; + default: + g_assert_not_reached(); + } + + if (ops && ops->init && !ops->init(s, errp)) { + return false; + } + s->s_accel->cmdqv_ops = ops; + return true; +} + static bool smmuv3_accel_set_iommu_device(PCIBus *bus, void *opaque, int devfn, HostIOMMUDevice *hiod, Error **errp) { @@ -XXX,XX +XXX,XX @@ static bool smmuv3_accel_set_iommu_device(PCIBus *bus, void *opaque, int devfn, goto done; } + if (!smmuv3_accel_select_cmdqv(s, hiodi, errp)) { + return false; + } + if (!smmuv3_accel_alloc_viommu(s, hiodi, errp)) { error_append_hint(errp, "Unable to alloc vIOMMU: hiodi devid 0x%x: ", hiodi->devid); @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_attach_gbpa_hwpt(SMMUv3State *s, Error **errp) void smmuv3_accel_reset(SMMUv3State *s) { - /* Attach a HWPT based on GBPA reset value */ - smmuv3_accel_attach_gbpa_hwpt(s, NULL); + SMMUv3AccelState *accel = s->s_accel; + + if (!accel) { + return; + } + /* Attach a HWPT based on GBPA reset value */ + smmuv3_accel_attach_gbpa_hwpt(s, NULL); + + if (accel->cmdqv_ops && accel->cmdqv_ops->reset) { + accel->cmdqv_ops->reset(s); + } } static void smmuv3_accel_as_init(SMMUv3State *s) diff --git a/include/hw/arm/smmuv3.h b/include/hw/arm/smmuv3.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/smmuv3.h +++ b/include/hw/arm/smmuv3.h @@ -XXX,XX +XXX,XX @@ struct SMMUv3State { OnOffAuto ats; OasMode oas; SsidSizeMode ssidsize; + /* SMMU CMDQV extension */ + OnOffAuto cmdqv; Notifier machine_done; }; -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> Introduce a GPtrArray in VirtMachineState to track all SMMUv3 devices created on the virt machine, and use it when building the IORT table instead of relying on object_child_foreach_recursive() walks of the object tree. This avoids recursive object traversal and provides a foundation for subsequent patches that need direct access to SMMUv3 instances for CMDQV-related handling. No functional change. No bios-tables qtest failures observed. Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-10-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/virt-acpi-build.c | 70 ++++++++++++++++++---------------------- hw/arm/virt.c | 3 ++ include/hw/arm/virt.h | 1 + 3 files changed, 35 insertions(+), 39 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -XXX,XX +XXX,XX @@ static int smmuv3_dev_idmap_compare(gconstpointer a, gconstpointer b) return map_a->input_base - map_b->input_base; } -static int iort_smmuv3_devices(Object *obj, void *opaque) -{ - VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine()); - AcpiIortSMMUv3Dev sdev = {0}; - GArray *sdev_blob = opaque; - AcpiIortIdMapping idmap; - PlatformBusDevice *pbus; - int min_bus, max_bus; - SysBusDevice *sbdev; - PCIBus *bus; - - if (!object_dynamic_cast(obj, TYPE_ARM_SMMUV3)) { - return 0; - } - - bus = PCI_BUS(object_property_get_link(obj, "primary-bus", &error_abort)); - sdev.accel = object_property_get_bool(obj, "accel", &error_abort); - sdev.ats = smmuv3_ats_enabled(ARM_SMMUV3(obj)); - pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); - sbdev = SYS_BUS_DEVICE(obj); - sdev.base = platform_bus_get_mmio_addr(pbus, sbdev, 0); - sdev.base += vms->memmap[VIRT_PLATFORM_BUS].base; - sdev.irq = platform_bus_get_irqn(pbus, sbdev, 0); - sdev.irq += vms->irqmap[VIRT_PLATFORM_BUS]; - sdev.irq += ARM_SPI_BASE; - - pci_bus_range(bus, &min_bus, &max_bus); - sdev.rc_smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); - idmap.input_base = min_bus << 8, - idmap.id_count = (max_bus - min_bus + 1) << 8, - g_array_append_val(sdev.rc_smmu_idmaps, idmap); - g_array_append_val(sdev_blob, sdev); - return 0; -} - /* * Populate the struct AcpiIortSMMUv3Dev for all SMMUv3 devices and * return the total number of idmaps. */ -static int populate_smmuv3_dev(GArray *sdev_blob) +static int populate_smmuv3_dev(VirtMachineState *vms, GArray *sdev_blob) { - object_child_foreach_recursive(object_get_root(), - iort_smmuv3_devices, sdev_blob); + for (int i = 0; i < vms->smmuv3_devices->len; i++) { + Object *obj = OBJECT(g_ptr_array_index(vms->smmuv3_devices, i)); + AcpiIortSMMUv3Dev sdev = {0}; + AcpiIortIdMapping idmap; + PlatformBusDevice *pbus; + int min_bus, max_bus; + SysBusDevice *sbdev; + PCIBus *bus; + + bus = PCI_BUS(object_property_get_link(obj, "primary-bus", + &error_abort)); + sdev.accel = object_property_get_bool(obj, "accel", &error_abort); + sdev.ats = smmuv3_ats_enabled(ARM_SMMUV3(obj)); + pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); + sbdev = SYS_BUS_DEVICE(obj); + sdev.base = platform_bus_get_mmio_addr(pbus, sbdev, 0); + sdev.base += vms->memmap[VIRT_PLATFORM_BUS].base; + sdev.irq = platform_bus_get_irqn(pbus, sbdev, 0); + sdev.irq += vms->irqmap[VIRT_PLATFORM_BUS]; + sdev.irq += ARM_SPI_BASE; + + pci_bus_range(bus, &min_bus, &max_bus); + sdev.rc_smmu_idmaps = g_array_new(false, true, + sizeof(AcpiIortIdMapping)); + idmap.input_base = min_bus << 8; + idmap.id_count = (max_bus - min_bus + 1) << 8; + g_array_append_val(sdev.rc_smmu_idmaps, idmap); + g_array_append_val(sdev_blob, sdev); + } /* Sort the smmuv3 devices(if any) by smmu idmap input_base */ g_array_sort(sdev_blob, smmuv3_dev_idmap_compare); /* @@ -XXX,XX +XXX,XX @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) if (vms->legacy_smmuv3_present) { rc_smmu_idmaps_len = populate_smmuv3_legacy_dev(smmuv3_devs); } else { - rc_smmu_idmaps_len = populate_smmuv3_dev(smmuv3_devs); + rc_smmu_idmaps_len = populate_smmuv3_dev(vms, smmuv3_devs); } num_smmus = smmuv3_devs->len; diff --git a/hw/arm/virt.c b/hw/arm/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -XXX,XX +XXX,XX @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, } create_smmuv3_dev_dtb(vms, dev, bus, errp); + g_ptr_array_add(vms->smmuv3_devices, dev); } } @@ -XXX,XX +XXX,XX @@ static void virt_instance_init(Object *obj) vms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); vms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); cxl_machine_init(obj, &vms->cxl_devices_state); + + vms->smmuv3_devices = g_ptr_array_new_with_free_func(NULL); } static void virt_instance_finalize(Object *obj) diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -XXX,XX +XXX,XX @@ struct VirtMachineState { MemoryRegion *sysmem; MemoryRegion *secure_sysmem; bool pci_preserve_config; + GPtrArray *smmuv3_devices; }; #define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM) -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> Use IOMMU_GET_HW_INFO to query host support for Tegra241 CMDQV. Validate the returned data type, version, and minimum number of vCMDQs and SIDs per Tegra241 CMDQ Virtual Interface(VI). Fail the probe if the host does not meet these requirements. The QEMU model supports one Virtual Interface(VI) per VM with 2 vCMDQs and 16 SIDs per VI, so the probe ensures the host implementation is compatible with these limits. Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-11-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/tegra241-cmdqv.c | 32 ++++++++++++++++++++++++++++++-- hw/arm/tegra241-cmdqv.h | 4 ++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.c +++ b/hw/arm/tegra241-cmdqv.c @@ -XXX,XX +XXX,XX @@ static bool tegra241_cmdqv_init(SMMUv3State *s, Error **errp) static bool tegra241_cmdqv_probe(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, Error **errp) { - error_setg(errp, "NVIDIA Tegra241 CMDQV is unsupported"); - return false; + uint32_t data_type = IOMMU_HW_INFO_TYPE_TEGRA241_CMDQV; + struct iommu_hw_info_tegra241_cmdqv cmdqv_info; + uint64_t caps; + + if (!iommufd_backend_get_device_info(idev->iommufd, idev->devid, &data_type, + &cmdqv_info, sizeof(cmdqv_info), &caps, + NULL, errp)) { + return false; + } + if (data_type != IOMMU_HW_INFO_TYPE_TEGRA241_CMDQV) { + error_setg(errp, "Host CMDQV: unexpected data type %u (expected %u)", + data_type, IOMMU_HW_INFO_TYPE_TEGRA241_CMDQV); + return false; + } + if (cmdqv_info.version != CMDQV_VER) { + error_setg(errp, "Host CMDQV: unsupported version %u (expected %u)", + cmdqv_info.version, CMDQV_VER); + return false; + } + if (cmdqv_info.log2vcmdqs < CMDQV_NUM_CMDQ_LOG2) { + error_setg(errp, "Host CMDQV: insufficient vCMDQs log2=%u (need >= %u)", + cmdqv_info.log2vcmdqs, CMDQV_NUM_CMDQ_LOG2); + return false; + } + if (cmdqv_info.log2vsids < CMDQV_NUM_SID_PER_VI_LOG2) { + error_setg(errp, "Host CMDQV: insufficient SIDs log2=%u (need >= %u)", + cmdqv_info.log2vsids, CMDQV_NUM_SID_PER_VI_LOG2); + return false; + } + return true; } static const SMMUv3AccelCmdqvOps tegra241_cmdqv_ops = { diff --git a/hw/arm/tegra241-cmdqv.h b/hw/arm/tegra241-cmdqv.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.h +++ b/hw/arm/tegra241-cmdqv.h @@ -XXX,XX +XXX,XX @@ #ifndef HW_ARM_TEGRA241_CMDQV_H #define HW_ARM_TEGRA241_CMDQV_H +#define CMDQV_VER 1 +#define CMDQV_NUM_CMDQ_LOG2 1 +#define CMDQV_NUM_SID_PER_VI_LOG2 4 + const SMMUv3AccelCmdqvOps *tegra241_cmdqv_get_ops(void); #endif /* HW_ARM_TEGRA241_CMDQV_H */ -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> Tegra241 CMDQV extends SMMUv3 with support for virtual command queues (VCMDQs) exposed via a CMDQV MMIO region. The CMDQV MMIO space is split into 64KB pages: 0x00000 (CMDQ-V Config page) 0x10000 (CMDQ-V CMDQ Page0) 0x20000 (CMDQ-V CMDQ Page1) 0x30000 (Virtual Interface Page0) 0x40000 (Virtual Interface Page1) This patch wires up the Tegra241 CMDQV init callback and allocates vendor-specific CMDQV state. The state pointer is stored in SMMUv3AccelState for use by subsequent CMDQV operations. The CMDQV MMIO region and a dedicated IRQ line are registered with the SMMUv3 device. The MMIO read/write handlers are currently stubs and will be implemented in later patches. The CMDQV interrupt is edge-triggered and indicates VCMDQ or VINTF error conditions. This patch only registers the IRQ line. Interrupt generation and propagation to the guest will be added in a subsequent patch. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Message-id: 20260609112552.378999-12-skolothumtho@nvidia.com Co-developed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-accel.h | 1 + hw/arm/tegra241-cmdqv.c | 40 ++++++++++++++++++++++++++++++++++++++-- hw/arm/tegra241-cmdqv.h | 20 ++++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/hw/arm/smmuv3-accel.h b/hw/arm/smmuv3-accel.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.h +++ b/hw/arm/smmuv3-accel.h @@ -XXX,XX +XXX,XX @@ typedef struct SMMUv3AccelState { bool auto_mode; bool auto_finalised; const SMMUv3AccelCmdqvOps *cmdqv_ops; + void *cmdqv; /* vendor specific CMDQV state */ } SMMUv3AccelState; typedef struct SMMUS1Hwpt { diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.c +++ b/hw/arm/tegra241-cmdqv.c @@ -XXX,XX +XXX,XX @@ #include "smmuv3-accel.h" #include "tegra241-cmdqv.h" +static uint64_t tegra241_cmdqv_read_mmio(void *opaque, hwaddr offset, + unsigned size) +{ + return 0; +} + +static void tegra241_cmdqv_write_mmio(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ +} + static void tegra241_cmdqv_free_viommu(SMMUv3State *s) { } @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_reset(SMMUv3State *s) { } +static const MemoryRegionOps mmio_cmdqv_ops = { + .read = tegra241_cmdqv_read_mmio, + .write = tegra241_cmdqv_write_mmio, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 8, + }, +}; + static bool tegra241_cmdqv_init(SMMUv3State *s, Error **errp) { - error_setg(errp, "NVIDIA Tegra241 CMDQV is unsupported"); - return false; + SysBusDevice *sbd = SYS_BUS_DEVICE(OBJECT(s)); + SMMUv3AccelState *accel = s->s_accel; + Tegra241CMDQV *cmdqv; + + cmdqv = g_new0(Tegra241CMDQV, 1); + cmdqv->cmdqv_data = g_new0(struct iommu_viommu_tegra241_cmdqv, 1); + memory_region_init_io(&cmdqv->mmio_cmdqv, OBJECT(s), &mmio_cmdqv_ops, cmdqv, + "tegra241-cmdqv", TEGRA241_CMDQV_IO_LEN); + sysbus_init_mmio(sbd, &cmdqv->mmio_cmdqv); + sysbus_init_irq(sbd, &cmdqv->irq); + cmdqv->s_accel = accel; + accel->cmdqv = cmdqv; + return true; } static bool tegra241_cmdqv_probe(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, diff --git a/hw/arm/tegra241-cmdqv.h b/hw/arm/tegra241-cmdqv.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.h +++ b/hw/arm/tegra241-cmdqv.h @@ -XXX,XX +XXX,XX @@ #define CMDQV_NUM_CMDQ_LOG2 1 #define CMDQV_NUM_SID_PER_VI_LOG2 4 +/* + * Tegra241 CMDQV MMIO layout (64KB pages) + * + * 0x00000 (CMDQ-V Config page) + * 0x10000 (CMDQ-V CMDQ Page0) + * 0x20000 (CMDQ-V CMDQ Page1) + * 0x30000 (Virtual Interface Page0) + * 0x40000 (Virtual Interface Page1) + */ +#define TEGRA241_CMDQV_IO_LEN 0x50000 + +struct iommu_viommu_tegra241_cmdqv; + +typedef struct Tegra241CMDQV { + struct iommu_viommu_tegra241_cmdqv *cmdqv_data; + SMMUv3AccelState *s_accel; + MemoryRegion mmio_cmdqv; + qemu_irq irq; +} Tegra241CMDQV; + const SMMUv3AccelCmdqvOps *tegra241_cmdqv_get_ops(void); #endif /* HW_ARM_TEGRA241_CMDQV_H */ -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> SMMUv3 devices with acceleration may enable CMDQV extensions after device realize. In that case, additional MMIO regions and IRQ lines may be registered but not yet mapped to the platform bus. Ensure SMMUv3 device resources are linked to the platform bus during machine_done(). This is safe to do unconditionally since the platform bus helpers skip resources that are already mapped. Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-13-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/virt.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -XXX,XX +XXX,XX @@ static void virt_build_smbios(VirtMachineState *vms) } } +/* + * SMMUv3 devices with acceleration may enable CMDQV extensions + * after device realize. In that case, additional MMIO regions and + * IRQ lines may be registered but not yet mapped to the platform bus. + * + * Ensure all resources are linked to the platform bus before final + * machine setup. + */ + +static void virt_smmuv3_dev_link_cmdqv(VirtMachineState *vms) +{ + for (int i = 0; i < vms->smmuv3_devices->len; i++) { + DeviceState *dev = g_ptr_array_index(vms->smmuv3_devices, i); + + platform_bus_link_device(PLATFORM_BUS_DEVICE(vms->platform_bus_dev), + SYS_BUS_DEVICE(dev)); + } +} + static void virt_machine_done(Notifier *notifier, void *data) { @@ -XXX,XX +XXX,XX @@ void virt_machine_done(Notifier *notifier, void *data) if (vms->cxl_devices_state.is_enabled) { cxl_fmws_link_targets(&error_fatal); } + + virt_smmuv3_dev_link_cmdqv(vms); + /* * If the user provided a dtb, we assume the dynamic sysbus nodes * already are integrated there. This corresponds to a use case where -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> Replace the stub implementation with real vIOMMU allocation for Tegra241 CMDQV. Allocate a matching vEVENTQ together with the vIOMMU, since it is specific to the Tegra241 CMDQV vIOMMU and used to receive CMDQV events. Free both objects on teardown. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Message-id: 20260609112552.378999-14-skolothumtho@nvidia.com Co-developed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/tegra241-cmdqv.c | 48 ++++++++++++++++++++++++++++++++++++++++- hw/arm/tegra241-cmdqv.h | 1 + 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.c +++ b/hw/arm/tegra241-cmdqv.c @@ -XXX,XX +XXX,XX @@ #include "qemu/osdep.h" #include "hw/arm/smmuv3.h" +#include "hw/arm/smmuv3-common.h" #include "smmuv3-accel.h" #include "tegra241-cmdqv.h" @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_write_mmio(void *opaque, hwaddr offset, static void tegra241_cmdqv_free_viommu(SMMUv3State *s) { + SMMUv3AccelState *accel = s->s_accel; + IOMMUFDViommu *viommu = accel->viommu; + Tegra241CMDQV *cmdqv = accel->cmdqv; + IOMMUFDVeventq *veventq = cmdqv->veventq; + + if (!viommu) { + return; + } + if (veventq) { + close(veventq->veventq_fd); + iommufd_backend_free_id(viommu->iommufd, veventq->veventq_id); + g_free(veventq); + cmdqv->veventq = NULL; + } + iommufd_backend_free_id(viommu->iommufd, viommu->viommu_id); } static bool tegra241_cmdqv_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, uint32_t *out_viommu_id, Error **errp) { - error_setg(errp, "NVIDIA Tegra241 CMDQV is unsupported"); + Tegra241CMDQV *cmdqv = s->s_accel->cmdqv; + uint32_t viommu_id, veventq_id, veventq_fd; + IOMMUFDVeventq *veventq; + + if (!iommufd_backend_alloc_viommu(idev->iommufd, idev->devid, + IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV, + idev->hwpt_id, cmdqv->cmdqv_data, + sizeof(*cmdqv->cmdqv_data), &viommu_id, + errp)) { + return false; + } + + if (!iommufd_backend_alloc_veventq(idev->iommufd, viommu_id, + IOMMU_VEVENTQ_TYPE_TEGRA241_CMDQV, + 1 << SMMU_EVENTQS, &veventq_id, + &veventq_fd, + errp)) { + error_append_hint(errp, "Tegra241 CMDQV: failed to alloc veventq"); + goto free_viommu; + } + + veventq = g_new(IOMMUFDVeventq, 1); + veventq->veventq_id = veventq_id; + veventq->veventq_fd = veventq_fd; + cmdqv->veventq = veventq; + + *out_viommu_id = viommu_id; + return true; + +free_viommu: + iommufd_backend_free_id(idev->iommufd, viommu_id); return false; } diff --git a/hw/arm/tegra241-cmdqv.h b/hw/arm/tegra241-cmdqv.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.h +++ b/hw/arm/tegra241-cmdqv.h @@ -XXX,XX +XXX,XX @@ typedef struct Tegra241CMDQV { SMMUv3AccelState *s_accel; MemoryRegion mmio_cmdqv; qemu_irq irq; + IOMMUFDVeventq *veventq; } Tegra241CMDQV; const SMMUv3AccelCmdqvOps *tegra241_cmdqv_get_ops(void); -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> The kernel currently exposes a single VINTF per emulated SMMUv3 instance. IOMMU_VIOMMU_ALLOC returns an mmap offset for the host VINTF Page0 allocated for this SMMU. However, VCMDQs only become bound to that VINTF after IOMMU_HW_QUEUE_ALLOC, so until then the mapped Page0 does not back any real VCMDQ state. mmap the host VINTF Page0 right after IOMMU_VIOMMU_ALLOC, as the host VINTF is already enabled at that point, and unmap it when the vIOMMU is freed. The mapping shares the vIOMMU's lifetime. This prepares the VINTF mapping in advance of subsequent patches that add VCMDQ allocation. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-15-skolothumtho@nvidia.com Co-developed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/tegra241-cmdqv.c | 16 +++++++++++++++- hw/arm/tegra241-cmdqv.h | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.c +++ b/hw/arm/tegra241-cmdqv.c @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_free_viommu(SMMUv3State *s) g_free(veventq); cmdqv->veventq = NULL; } + if (cmdqv->vintf_page0) { + munmap(cmdqv->vintf_page0, VINTF_PAGE_SIZE); + cmdqv->vintf_page0 = NULL; + } iommufd_backend_free_id(viommu->iommufd, viommu->viommu_id); } @@ -XXX,XX +XXX,XX @@ tegra241_cmdqv_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, return false; } + if (!iommufd_backend_viommu_mmap(idev->iommufd, viommu_id, VINTF_PAGE_SIZE, + cmdqv->cmdqv_data->out_vintf_mmap_offset, + &cmdqv->vintf_page0, errp)) { + error_append_hint(errp, "Tegra241 CMDQV: failed to mmap VINTF page0"); + goto free_viommu; + } + if (!iommufd_backend_alloc_veventq(idev->iommufd, viommu_id, IOMMU_VEVENTQ_TYPE_TEGRA241_CMDQV, 1 << SMMU_EVENTQS, &veventq_id, &veventq_fd, errp)) { error_append_hint(errp, "Tegra241 CMDQV: failed to alloc veventq"); - goto free_viommu; + goto munmap_page0; } veventq = g_new(IOMMUFDVeventq, 1); @@ -XXX,XX +XXX,XX @@ tegra241_cmdqv_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, *out_viommu_id = viommu_id; return true; +munmap_page0: + munmap(cmdqv->vintf_page0, VINTF_PAGE_SIZE); + cmdqv->vintf_page0 = NULL; free_viommu: iommufd_backend_free_id(idev->iommufd, viommu_id); return false; diff --git a/hw/arm/tegra241-cmdqv.h b/hw/arm/tegra241-cmdqv.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.h +++ b/hw/arm/tegra241-cmdqv.h @@ -XXX,XX +XXX,XX @@ */ #define TEGRA241_CMDQV_IO_LEN 0x50000 +#define VINTF_PAGE_SIZE 0x10000 + struct iommu_viommu_tegra241_cmdqv; typedef struct Tegra241CMDQV { @@ -XXX,XX +XXX,XX @@ typedef struct Tegra241CMDQV { MemoryRegion mmio_cmdqv; qemu_irq irq; IOMMUFDVeventq *veventq; + void *vintf_page0; } Tegra241CMDQV; const SMMUv3AccelCmdqvOps *tegra241_cmdqv_get_ops(void); -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> Tegra241 CMDQV exposes control and status registers in the CMDQ-V Config page (offset [0x0, 0x10000)) used to configure virtual command queue allocation and interrupt behavior. Add read/write emulation for the CMDQ-V Config region ([CMDQV_BASE, CMDQV_CMDQ_BASE]), backed by a simple register cache. This includes CONFIG, PARAM, STATUS, VI error and interrupt maps, CMDQ allocation map and the VINTF0 related registers defined in the CMDQ-V Config space. Only VINTF0 is supported; VINTF1-63 are not. Dispatch writes on access size: Introduced writel_mmio for 4-byte and writell_mmio for 8-byte. Reads need no split as the MMIO framework masks the returned value to the access size. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Message-id: 20260609112552.378999-16-skolothumtho@nvidia.com Co-developed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/tegra241-cmdqv.c | 181 +++++++++++++++++++++++++++++++++++++++- hw/arm/tegra241-cmdqv.h | 110 ++++++++++++++++++++++++ hw/arm/trace-events | 4 + 3 files changed, 294 insertions(+), 1 deletion(-) diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.c +++ b/hw/arm/tegra241-cmdqv.c @@ -XXX,XX +XXX,XX @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/arm/smmuv3.h" #include "hw/arm/smmuv3-common.h" #include "smmuv3-accel.h" #include "tegra241-cmdqv.h" +#include "trace.h" + +static uint64_t tegra241_cmdqv_config_vintf_read(Tegra241CMDQV *cmdqv, + hwaddr offset) +{ + int i; + + switch (offset) { + case A_VINTF0_CONFIG: + return cmdqv->vintf_config; + case A_VINTF0_STATUS: + return cmdqv->vintf_status; + case A_VINTF0_SID_MATCH_0 ... A_VINTF0_SID_MATCH_15: + i = (offset - A_VINTF0_SID_MATCH_0) / 4; + return cmdqv->vintf_sid_match[i]; + case A_VINTF0_SID_REPLACE_0 ... A_VINTF0_SID_REPLACE_15: + i = (offset - A_VINTF0_SID_REPLACE_0) / 4; + return cmdqv->vintf_sid_replace[i]; + case A_VINTF0_LVCMDQ_ERR_MAP_0 ... A_VINTF0_LVCMDQ_ERR_MAP_3: + i = (offset - A_VINTF0_LVCMDQ_ERR_MAP_0) / 4; + return cmdqv->vintf_cmdq_err_map[i]; + default: + /* + * GLB_FILT_CFG_0 (offset 0xC) and GLB_FILT_DATA_0 (offset 0x10) are + * filter config and filter data registers. They are not required for + * normal VINTF operation and are not emulated. + */ + qemu_log_mask(LOG_UNIMP, "%s unhandled read access at 0x%" PRIx64 "\n", + __func__, offset); + return 0; + } +} + +static void tegra241_cmdqv_config_vintf_write(Tegra241CMDQV *cmdqv, + hwaddr offset, uint64_t value) +{ + int i; + + switch (offset) { + case A_VINTF0_CONFIG: + /* + * Mask out HYP_OWN on guest writes. This bit selects Hypervisor (1) vs + * Guest (0) ownership of the CMDQ. Force it to 0 so the VINTF always + * remains guest-owned. + */ + value &= ~R_VINTF0_CONFIG_HYP_OWN_MASK; + + cmdqv->vintf_config = value; + if (value & R_VINTF0_CONFIG_ENABLE_MASK) { + cmdqv->vintf_status |= R_VINTF0_STATUS_ENABLE_OK_MASK; + } else { + cmdqv->vintf_status &= ~R_VINTF0_STATUS_ENABLE_OK_MASK; + } + break; + case A_VINTF0_SID_MATCH_0 ... A_VINTF0_SID_MATCH_15: + i = (offset - A_VINTF0_SID_MATCH_0) / 4; + cmdqv->vintf_sid_match[i] = value; + break; + case A_VINTF0_SID_REPLACE_0 ... A_VINTF0_SID_REPLACE_15: + i = (offset - A_VINTF0_SID_REPLACE_0) / 4; + cmdqv->vintf_sid_replace[i] = value; + break; + default: + /* + * GLB_FILT_CFG_0 (offset 0xC) and GLB_FILT_DATA_0 (offset 0x10) are + * filter config and filter data registers. They are not required for + * normal VINTF operation and are not emulated. + */ + qemu_log_mask(LOG_UNIMP, "%s unhandled write access at 0x%" PRIx64 "\n", + __func__, offset); + return; + } +} static uint64_t tegra241_cmdqv_read_mmio(void *opaque, hwaddr offset, unsigned size) { - return 0; + Tegra241CMDQV *cmdqv = (Tegra241CMDQV *)opaque; + uint64_t val = 0; + + if (offset >= TEGRA241_CMDQV_IO_LEN) { + qemu_log_mask(LOG_UNIMP, + "%s offset 0x%" PRIx64 " off limit (0x%x)\n", __func__, + offset, TEGRA241_CMDQV_IO_LEN); + goto out; + } + + switch (offset) { + case A_CONFIG: + val = cmdqv->config; + break; + case A_PARAM: + val = cmdqv->param; + break; + case A_STATUS: + val = cmdqv->status; + break; + case A_VI_ERR_MAP_0 ... A_VI_ERR_MAP_1: + val = cmdqv->vi_err_map[(offset - A_VI_ERR_MAP_0) / 4]; + break; + case A_VI_INT_MASK_0 ... A_VI_INT_MASK_1: + val = cmdqv->vi_int_mask[(offset - A_VI_INT_MASK_0) / 4]; + break; + case A_CMDQ_ERR_MAP_0 ... A_CMDQ_ERR_MAP_3: + val = cmdqv->cmdq_err_map[(offset - A_CMDQ_ERR_MAP_0) / 4]; + break; + case A_CMDQ_ALLOC_MAP_0 ... A_CMDQ_ALLOC_MAP_1: + val = cmdqv->cmdq_alloc_map[(offset - A_CMDQ_ALLOC_MAP_0) / 4]; + break; + case A_VINTF0_CONFIG ... A_VINTF0_LVCMDQ_ERR_MAP_3: + val = tegra241_cmdqv_config_vintf_read(cmdqv, offset); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s unhandled read access at 0x%" PRIx64 "\n", + __func__, offset); + } + +out: + trace_tegra241_cmdqv_read_mmio(offset, val, size); + return val; +} + +/* 4-byte MMIO write handler. */ +static void tegra241_cmdqv_writel_mmio(Tegra241CMDQV *cmdqv, hwaddr offset, + uint32_t value) +{ + switch (offset) { + case A_CONFIG: + cmdqv->config = value; + if (value & R_CONFIG_CMDQV_EN_MASK) { + cmdqv->status |= R_STATUS_CMDQV_ENABLED_MASK; + } else { + cmdqv->status &= ~R_STATUS_CMDQV_ENABLED_MASK; + } + break; + case A_VI_INT_MASK_0 ... A_VI_INT_MASK_1: + cmdqv->vi_int_mask[(offset - A_VI_INT_MASK_0) / 4] = value; + break; + case A_CMDQ_ALLOC_MAP_0 ... A_CMDQ_ALLOC_MAP_1: + cmdqv->cmdq_alloc_map[(offset - A_CMDQ_ALLOC_MAP_0) / 4] = value; + break; + case A_VINTF0_CONFIG ... A_VINTF0_LVCMDQ_ERR_MAP_3: + tegra241_cmdqv_config_vintf_write(cmdqv, offset, value); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s unhandled write access at 0x%" PRIx64 "\n", + __func__, offset); + } +} + +/* + * 8-byte MMIO write handler. + */ +static void tegra241_cmdqv_writell_mmio(Tegra241CMDQV *cmdqv, hwaddr offset, + uint64_t value) +{ + qemu_log_mask(LOG_UNIMP, + "%s unhandled 64-bit write at 0x%" PRIx64 " (WI)\n", + __func__, offset); } static void tegra241_cmdqv_write_mmio(void *opaque, hwaddr offset, uint64_t value, unsigned size) { + Tegra241CMDQV *cmdqv = (Tegra241CMDQV *)opaque; + + if (offset >= TEGRA241_CMDQV_IO_LEN) { + qemu_log_mask(LOG_UNIMP, + "%s offset 0x%" PRIx64 " off limit (0x%x)\n", __func__, + offset, TEGRA241_CMDQV_IO_LEN); + goto out; + } + + switch (size) { + case 4: + tegra241_cmdqv_writel_mmio(cmdqv, offset, value); + break; + case 8: + tegra241_cmdqv_writell_mmio(cmdqv, offset, value); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s bad write size %u at 0x%" PRIx64 "\n", + __func__, size, offset); + } + +out: + trace_tegra241_cmdqv_write_mmio(offset, value, size); } static void tegra241_cmdqv_free_viommu(SMMUv3State *s) diff --git a/hw/arm/tegra241-cmdqv.h b/hw/arm/tegra241-cmdqv.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.h +++ b/hw/arm/tegra241-cmdqv.h @@ -XXX,XX +XXX,XX @@ #ifndef HW_ARM_TEGRA241_CMDQV_H #define HW_ARM_TEGRA241_CMDQV_H +#include "hw/core/registerfields.h" + #define CMDQV_VER 1 #define CMDQV_NUM_CMDQ_LOG2 1 #define CMDQV_NUM_SID_PER_VI_LOG2 4 +#define TEGRA241_CMDQV_MAX_CMDQ (1U << CMDQV_NUM_CMDQ_LOG2) +#define TEGRA241_CMDQV_MAX_NUM_SID (1U << CMDQV_NUM_SID_PER_VI_LOG2) + /* * Tegra241 CMDQV MMIO layout (64KB pages) * @@ -XXX,XX +XXX,XX @@ typedef struct Tegra241CMDQV { qemu_irq irq; IOMMUFDVeventq *veventq; void *vintf_page0; + + /* CMDQ-V Config page register cache */ + uint32_t config; + uint32_t param; + uint32_t status; + uint32_t vi_err_map[2]; + uint32_t vi_int_mask[2]; + uint32_t cmdq_err_map[4]; + uint32_t cmdq_alloc_map[TEGRA241_CMDQV_MAX_CMDQ]; + + /* VINTF0 register cache (within CMDQ-V Config page) */ + uint32_t vintf_config; + uint32_t vintf_status; + uint32_t vintf_sid_match[TEGRA241_CMDQV_MAX_NUM_SID]; + uint32_t vintf_sid_replace[TEGRA241_CMDQV_MAX_NUM_SID]; + uint32_t vintf_cmdq_err_map[4]; } Tegra241CMDQV; +/* CMDQ-V Config page registers (offset 0x00000) */ +REG32(CONFIG, 0x0) +FIELD(CONFIG, CMDQV_EN, 0, 1) +FIELD(CONFIG, CMDQV_PER_CMD_OFFSET, 1, 3) +FIELD(CONFIG, CMDQ_MAX_CLK_BATCH, 4, 8) +FIELD(CONFIG, CMDQ_MAX_CMD_BATCH, 12, 8) +FIELD(CONFIG, CONS_DRAM_EN, 20, 1) + +REG32(PARAM, 0x4) +FIELD(PARAM, CMDQV_VER, 0, 4) +FIELD(PARAM, CMDQV_NUM_CMDQ_LOG2, 4, 4) +FIELD(PARAM, CMDQV_NUM_VI_LOG2, 8, 4) +FIELD(PARAM, CMDQV_NUM_SID_PER_VI_LOG2, 12, 4) + +REG32(STATUS, 0x8) +FIELD(STATUS, CMDQV_ENABLED, 0, 1) + +/* SMMU_CMDQV_VI_ERR_MAP_0/1 definitions */ +#define A_VI_ERR_MAP_0 0x14 +#define A_VI_ERR_MAP_1 0x18 +#define V_VI_ERR_MAP_NO_ERROR (0) +#define V_VI_ERR_MAP_ERROR (1) + +/* SMMU_CMDQV_VI_INT_MASK_0/1 definitions */ +#define A_VI_INT_MASK_0 0x1c +#define A_VI_INT_MASK_1 0x20 +#define V_VI_INT_MASK_NOT_MASKED (0) +#define V_VI_INT_MASK_MASKED (1) + +/* SMMU_CMDQV_CMDQ_ERR_MAP_0-3 definitions */ +#define A_CMDQ_ERR_MAP_0 0x24 +#define A_CMDQ_ERR_MAP_1 0x28 +#define A_CMDQ_ERR_MAP_2 0x2c +#define A_CMDQ_ERR_MAP_3 0x30 + +/* + * CMDQ_ALLOC_MAP: one entry per physical VCMDQ. Hardware supports up to 128 + * entries (CMDQV_NUM_CMDQ_LOG2=7), but QEMU only exposes + * TEGRA241_CMDQV_MAX_CMDQ (=2) VCMDQs per VM so only entries 0 and 1 are + * defined here. + */ +/* 2 identical register entries */ +#define SMMU_CMDQV_CMDQ_ALLOC_MAP_(i) \ + REG32(CMDQ_ALLOC_MAP_##i, 0x200 + i * 4) \ + FIELD(CMDQ_ALLOC_MAP_##i, ALLOC, 0, 1) \ + FIELD(CMDQ_ALLOC_MAP_##i, LVCMDQ, 1, 7) \ + FIELD(CMDQ_ALLOC_MAP_##i, VIRT_INTF_INDX, 15, 6) + +SMMU_CMDQV_CMDQ_ALLOC_MAP_(0) +SMMU_CMDQV_CMDQ_ALLOC_MAP_(1) + +/* SMMU_CMDQV_VINTF0 registers (only VINTF0 is exposed to the guest) */ +REG32(VINTF0_CONFIG, 0x1000) +FIELD(VINTF0_CONFIG, ENABLE, 0, 1) +FIELD(VINTF0_CONFIG, VMID, 1, 16) +FIELD(VINTF0_CONFIG, HYP_OWN, 17, 1) + +REG32(VINTF0_STATUS, 0x1004) +FIELD(VINTF0_STATUS, ENABLE_OK, 0, 1) +FIELD(VINTF0_STATUS, STATUS, 1, 3) +FIELD(VINTF0_STATUS, VI_NUM_LVCMDQ, 16, 8) + +#define V_VINTF_STATUS_NO_ERROR (0 << 1) +#define V_VINTF_STATUS_VCMDQ_ERROR (1 << 1) + +/* + * SMMU_CMDQV_VINTF0_SID_MATCH/_REPLACE: 16 entries per VINTF + * (CMDQV_NUM_SID_PER_VI_LOG2=4). Only _0 and _15 are defined, + * used as switch case range bounds. + */ +REG32(VINTF0_SID_MATCH_0, 0x1040) +FIELD(VINTF0_SID_MATCH_0, ENABLE, 0, 1) +FIELD(VINTF0_SID_MATCH_0, VIRT_SID, 1, 20) +#define A_VINTF0_SID_MATCH_15 (A_VINTF0_SID_MATCH_0 + 15 * 4) + +REG32(VINTF0_SID_REPLACE_0, 0x1080) +FIELD(VINTF0_SID_REPLACE_0, PHYS_SID, 0, 20) +#define A_VINTF0_SID_REPLACE_15 (A_VINTF0_SID_REPLACE_0 + 15 * 4) + +/* + * SMMU_CMDQV_VINTF0_LVCMDQ_ERR_MAP: 4 registers per VINTF covering 32 logical + * VCMDQs each. With TEGRA241_CMDQV_MAX_CMDQ=2, only MAP_0 bits [1:0] carry + * error state. MAP_1..MAP_3 always read as 0. Only _0 and _3 are defined, + * used as switch case range bounds. + */ +REG32(VINTF0_LVCMDQ_ERR_MAP_0, 0x10c0) +FIELD(VINTF0_LVCMDQ_ERR_MAP_0, LVCMDQ_ERR_MAP, 0, 32) +#define A_VINTF0_LVCMDQ_ERR_MAP_3 (A_VINTF0_LVCMDQ_ERR_MAP_0 + 3 * 4) + const SMMUv3AccelCmdqvOps *tegra241_cmdqv_get_ops(void); #endif /* HW_ARM_TEGRA241_CMDQV_H */ diff --git a/hw/arm/trace-events b/hw/arm/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -XXX,XX +XXX,XX @@ smmuv3_accel_unset_iommu_device(int devfn, uint32_t devid) "devfn=0x%x (idev dev smmuv3_accel_translate_ste(uint32_t vsid, uint32_t hwpt_id, uint64_t ste_1, uint64_t ste_0) "vSID=0x%x hwpt_id=0x%x ste=%"PRIx64":%"PRIx64 smmuv3_accel_install_ste(uint32_t vsid, const char * type, uint32_t hwpt_id) "vSID=0x%x ste type=%s hwpt_id=0x%x" +# tegra241-cmdqv +tegra241_cmdqv_read_mmio(uint64_t offset, uint64_t val, unsigned size) "offset: 0x%"PRIx64" val: 0x%"PRIx64" size: 0x%x" +tegra241_cmdqv_write_mmio(uint64_t offset, uint64_t val, unsigned size) "offset: 0x%"PRIx64" val: 0x%"PRIx64" size: 0x%x" + # strongarm.c strongarm_uart_update_parameters(const char *label, int speed, char parity, int data_bits, int stop_bits) "%s speed=%d parity=%c data=%d stop=%d" strongarm_ssp_read_underrun(void) "SSP rx underrun" -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> Tegra241 CMDQV exposes per-VCMDQ register windows through two MMIO apertures: Direct VCMDQ aperture (0x10000/0x20000): VCMDQ Page0/Page1 VINTF logical aperture (0x30000/0x40000): VINTF0 LVCMDQ Page0/Page1 Both apertures are hardware aliases of the same underlying registers: Page 0 (control/status): CONS_INDX, PROD_INDX, CONFIG, STATUS, GERROR, GERRORN Page 1 (base/DRAM): BASE_L/H, CONS_INDX_BASE_DRAM_L/H The direct aperture Page 0 is programmable at any time so long as CMDQV_EN is enabled. The VINTF (logical) aperture Page 0 is programmable only once SW has mapped a VCMDQ to a VINTF; the "logical" view is local to that VINTF. Add read emulation for both apertures, backed by a single per-VCMDQ register cache. VINTF aperture reads are translated to their equivalent direct-aperture offset and served from the same cached state. Per the CMDQV architecture, a VCMDQ must be allocated to a Virtual Interface before it is used to send commands to the SMMU. Until that allocation happens, reads return cached register state with no HW interaction. Subsequent patches wire up IOMMU_HW_QUEUE_ALLOC, mmap the host VINTF Page 0, and install it into guest MMIO; after that, Page 0 reads from either aperture are served from the hardware-backed mmap'd page instead of the cache. Page 1 is also a hardware alias, but the kernel only exposes mmap for Page 0, so Page 1 reads always trap to QEMU and are served from cache. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Message-id: 20260609112552.378999-17-skolothumtho@nvidia.com Co-developed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/tegra241-cmdqv.c | 103 +++++++++++++++++++ hw/arm/tegra241-cmdqv.h | 216 ++++++++++++++++++++++++++++++++++++++++ hw/arm/trace-events | 2 + 3 files changed, 321 insertions(+) diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.c +++ b/hw/arm/tegra241-cmdqv.c @@ -XXX,XX +XXX,XX @@ #include "tegra241-cmdqv.h" #include "trace.h" +/* + * Read a VCMDQ Page 0 register (control/status) using VCMDQ0_* offsets. + * + * The caller normalizes the MMIO offset such that @offset0 always refers + * to a VCMDQ0_* register, while @index selects the VCMDQ instance. + */ +static uint64_t tegra241_cmdqv_read_vcmdq_page0(Tegra241CMDQV *cmdqv, + hwaddr offset0, int index, + bool direct) +{ + uint64_t val = 0; + + switch (offset0) { + case A_VCMDQ0_CONS_INDX: + val = cmdqv->vcmdq_cons_indx[index]; + break; + case A_VCMDQ0_PROD_INDX: + val = cmdqv->vcmdq_prod_indx[index]; + break; + case A_VCMDQ0_CONFIG: + val = cmdqv->vcmdq_config[index]; + break; + case A_VCMDQ0_STATUS: + val = cmdqv->vcmdq_status[index]; + break; + case A_VCMDQ0_GERROR: + val = cmdqv->vcmdq_gerror[index]; + break; + case A_VCMDQ0_GERRORN: + val = cmdqv->vcmdq_gerrorn[index]; + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s unhandled read access at 0x%" PRIx64 "\n", + __func__, offset0); + } + trace_tegra241_cmdqv_read_vcmdq_page0(index, direct ? "direct" : "vi", + offset0, val); + return val; +} + +/* + * Read a VCMDQ Page 1 register (base / DRAM address) using VCMDQ0_* offsets. + */ +static uint64_t tegra241_cmdqv_read_vcmdq_page1(Tegra241CMDQV *cmdqv, + hwaddr offset0, int index, + bool direct) +{ + uint64_t val = 0; + + switch (offset0) { + case A_VCMDQ0_BASE_L: + val = cmdqv->vcmdq_base[index]; + break; + case A_VCMDQ0_BASE_H: + val = cmdqv->vcmdq_base[index] >> 32; + break; + case A_VCMDQ0_CONS_INDX_BASE_DRAM_L: + val = cmdqv->vcmdq_cons_indx_base[index]; + break; + case A_VCMDQ0_CONS_INDX_BASE_DRAM_H: + val = cmdqv->vcmdq_cons_indx_base[index] >> 32; + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s unhandled read access at 0x%" PRIx64 "\n", + __func__, offset0); + } + trace_tegra241_cmdqv_read_vcmdq_page1(index, direct ? "direct" : "vi", + offset0, val); + return val; +} + static uint64_t tegra241_cmdqv_config_vintf_read(Tegra241CMDQV *cmdqv, hwaddr offset) { @@ -XXX,XX +XXX,XX @@ static uint64_t tegra241_cmdqv_read_mmio(void *opaque, hwaddr offset, { Tegra241CMDQV *cmdqv = (Tegra241CMDQV *)opaque; uint64_t val = 0; + int index; if (offset >= TEGRA241_CMDQV_IO_LEN) { qemu_log_mask(LOG_UNIMP, @@ -XXX,XX +XXX,XX @@ static uint64_t tegra241_cmdqv_read_mmio(void *opaque, hwaddr offset, case A_VINTF0_CONFIG ... A_VINTF0_LVCMDQ_ERR_MAP_3: val = tegra241_cmdqv_config_vintf_read(cmdqv, offset); break; + case A_VI_VCMDQ0_CONS_INDX ... A_VI_VCMDQ1_GERRORN: + /* + * VINTF Page0 registers are hardware aliases of VCMDQ Page0 registers. + * Translate the VINTF aperture offset to its VCMDQ Page0 equivalent + * before dispatching to the Page 0 helper. + */ + offset -= CMDQV_VINTF_PAGE0_BASE - CMDQV_VCMDQ_PAGE0_BASE; + index = (offset - CMDQV_VCMDQ_PAGE0_BASE) / CMDQV_VCMDQ_STRIDE; + return tegra241_cmdqv_read_vcmdq_page0(cmdqv, + offset - index * CMDQV_VCMDQ_STRIDE, index, false); + case A_VCMDQ0_CONS_INDX ... A_VCMDQ1_GERRORN: + /* + * Decode a per-VCMDQ Page 0 access. Each VCMDQ occupies a + * CMDQV_VCMDQ_STRIDE-byte window; extract the index and normalize + * to the VCMDQ0_* offset before calling the Page 0 helper. + */ + index = (offset - CMDQV_VCMDQ_PAGE0_BASE) / CMDQV_VCMDQ_STRIDE; + return tegra241_cmdqv_read_vcmdq_page0(cmdqv, + offset - index * CMDQV_VCMDQ_STRIDE, index, true); + case A_VI_VCMDQ0_BASE_L ... A_VI_VCMDQ1_CONS_INDX_BASE_DRAM_H: + /* Same VINTF-to-VCMDQ translation as VINTF Page0 case above. */ + offset -= CMDQV_VINTF_PAGE1_BASE - CMDQV_VCMDQ_PAGE1_BASE; + index = (offset - CMDQV_VCMDQ_PAGE1_BASE) / CMDQV_VCMDQ_STRIDE; + return tegra241_cmdqv_read_vcmdq_page1(cmdqv, + offset - index * CMDQV_VCMDQ_STRIDE, index, false); + case A_VCMDQ0_BASE_L ... A_VCMDQ1_CONS_INDX_BASE_DRAM_H: + index = (offset - CMDQV_VCMDQ_PAGE1_BASE) / CMDQV_VCMDQ_STRIDE; + return tegra241_cmdqv_read_vcmdq_page1(cmdqv, + offset - index * CMDQV_VCMDQ_STRIDE, index, true); default: qemu_log_mask(LOG_UNIMP, "%s unhandled read access at 0x%" PRIx64 "\n", __func__, offset); diff --git a/hw/arm/tegra241-cmdqv.h b/hw/arm/tegra241-cmdqv.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.h +++ b/hw/arm/tegra241-cmdqv.h @@ -XXX,XX +XXX,XX @@ */ #define TEGRA241_CMDQV_IO_LEN 0x50000 +/* CMDQV MMIO aperture bases and VCMDQ stride */ +#define CMDQV_VCMDQ_PAGE0_BASE 0x10000 /* CMDQV_CMDQ_BASE */ +#define CMDQV_VCMDQ_PAGE1_BASE 0x20000 +#define CMDQV_VINTF_PAGE0_BASE 0x30000 /* CMDQV_VI_CMDQ_BASE */ +#define CMDQV_VINTF_PAGE1_BASE 0x40000 +#define CMDQV_VCMDQ_STRIDE 0x80 + #define VINTF_PAGE_SIZE 0x10000 struct iommu_viommu_tegra241_cmdqv; @@ -XXX,XX +XXX,XX @@ typedef struct Tegra241CMDQV { uint32_t vintf_sid_match[TEGRA241_CMDQV_MAX_NUM_SID]; uint32_t vintf_sid_replace[TEGRA241_CMDQV_MAX_NUM_SID]; uint32_t vintf_cmdq_err_map[4]; + /* + * VCMDQ register cache. The direct (VCMDQ aperture) and logical + * (VINTF aperture) views are hardware aliases; both are served from + * this single cached copy. + */ + uint32_t vcmdq_cons_indx[TEGRA241_CMDQV_MAX_CMDQ]; + uint32_t vcmdq_prod_indx[TEGRA241_CMDQV_MAX_CMDQ]; + uint32_t vcmdq_config[TEGRA241_CMDQV_MAX_CMDQ]; + uint32_t vcmdq_status[TEGRA241_CMDQV_MAX_CMDQ]; + uint32_t vcmdq_gerror[TEGRA241_CMDQV_MAX_CMDQ]; + uint32_t vcmdq_gerrorn[TEGRA241_CMDQV_MAX_CMDQ]; + uint64_t vcmdq_base[TEGRA241_CMDQV_MAX_CMDQ]; + uint64_t vcmdq_cons_indx_base[TEGRA241_CMDQV_MAX_CMDQ]; } Tegra241CMDQV; /* CMDQ-V Config page registers (offset 0x00000) */ @@ -XXX,XX +XXX,XX @@ REG32(VINTF0_LVCMDQ_ERR_MAP_0, 0x10c0) FIELD(VINTF0_LVCMDQ_ERR_MAP_0, LVCMDQ_ERR_MAP, 0, 32) #define A_VINTF0_LVCMDQ_ERR_MAP_3 (A_VINTF0_LVCMDQ_ERR_MAP_0 + 3 * 4) +/* + * Direct VCMDQ aperture register windows. + * + * Page 0 @ CMDQV_VCMDQ_PAGE0_BASE: VCMDQ control and status registers. + * Page 1 @ CMDQV_VCMDQ_PAGE1_BASE: VCMDQ base and DRAM address registers. + * + * Each VCMDQ occupies a CMDQV_VCMDQ_STRIDE-byte slot within its page. + */ + +/* --- Page 0 register macros --- */ +#define SMMU_CMDQV_VCMDQi_CONS_INDX_(i) \ + REG32(VCMDQ##i##_CONS_INDX, \ + CMDQV_VCMDQ_PAGE0_BASE + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VCMDQ##i##_CONS_INDX, RD, 0, 20) \ + FIELD(VCMDQ##i##_CONS_INDX, ERR, 24, 7) + +#define V_VCMDQ_CONS_INDX_ERR_CERROR_NONE 0 +#define V_VCMDQ_CONS_INDX_ERR_CERROR_ILL_OPCODE 1 +#define V_VCMDQ_CONS_INDX_ERR_CERROR_ABT 2 +#define V_VCMDQ_CONS_INDX_ERR_CERROR_ATC_INV_SYNC 3 +#define V_VCMDQ_CONS_INDX_ERR_CERROR_ILL_ACCESS 4 + +#define SMMU_CMDQV_VCMDQi_PROD_INDX_(i) \ + REG32(VCMDQ##i##_PROD_INDX, \ + CMDQV_VCMDQ_PAGE0_BASE + 0x4 + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VCMDQ##i##_PROD_INDX, WR, 0, 20) + +#define SMMU_CMDQV_VCMDQi_CONFIG_(i) \ + REG32(VCMDQ##i##_CONFIG, \ + CMDQV_VCMDQ_PAGE0_BASE + 0x8 + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VCMDQ##i##_CONFIG, CMDQ_EN, 0, 1) + +#define SMMU_CMDQV_VCMDQi_STATUS_(i) \ + REG32(VCMDQ##i##_STATUS, \ + CMDQV_VCMDQ_PAGE0_BASE + 0xc + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VCMDQ##i##_STATUS, CMDQ_EN_OK, 0, 1) + +#define SMMU_CMDQV_VCMDQi_GERROR_(i) \ + REG32(VCMDQ##i##_GERROR, \ + CMDQV_VCMDQ_PAGE0_BASE + 0x10 + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VCMDQ##i##_GERROR, CMDQ_ERR, 0, 1) \ + FIELD(VCMDQ##i##_GERROR, CONS_DRAM_WR_ABT_ERR, 1, 1) \ + FIELD(VCMDQ##i##_GERROR, CMDQ_INIT_ERR, 2, 1) + +#define SMMU_CMDQV_VCMDQi_GERRORN_(i) \ + REG32(VCMDQ##i##_GERRORN, \ + CMDQV_VCMDQ_PAGE0_BASE + 0x14 + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VCMDQ##i##_GERRORN, CMDQ_ERR, 0, 1) \ + FIELD(VCMDQ##i##_GERRORN, CONS_DRAM_WR_ABT_ERR, 1, 1) \ + FIELD(VCMDQ##i##_GERRORN, CMDQ_INIT_ERR, 2, 1) + +/* Page 0 layout: VCMDQ0 */ +SMMU_CMDQV_VCMDQi_CONS_INDX_(0) +SMMU_CMDQV_VCMDQi_PROD_INDX_(0) +SMMU_CMDQV_VCMDQi_CONFIG_(0) +SMMU_CMDQV_VCMDQi_STATUS_(0) +SMMU_CMDQV_VCMDQi_GERROR_(0) +SMMU_CMDQV_VCMDQi_GERRORN_(0) + +/* Page 0 layout: VCMDQ1 */ +SMMU_CMDQV_VCMDQi_CONS_INDX_(1) +SMMU_CMDQV_VCMDQi_PROD_INDX_(1) +SMMU_CMDQV_VCMDQi_CONFIG_(1) +SMMU_CMDQV_VCMDQi_STATUS_(1) +SMMU_CMDQV_VCMDQi_GERROR_(1) +SMMU_CMDQV_VCMDQi_GERRORN_(1) + +/* --- Page 1 register macros --- */ +#define SMMU_CMDQV_VCMDQi_BASE_L_(i) \ + REG32(VCMDQ##i##_BASE_L, CMDQV_VCMDQ_PAGE1_BASE + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VCMDQ##i##_BASE_L, LOG2SIZE, 0, 5) \ + FIELD(VCMDQ##i##_BASE_L, ADDR, 5, 27) + +#define SMMU_CMDQV_VCMDQi_BASE_H_(i) \ + REG32(VCMDQ##i##_BASE_H, \ + CMDQV_VCMDQ_PAGE1_BASE + 0x4 + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VCMDQ##i##_BASE_H, ADDR, 0, 16) + +#define SMMU_CMDQV_VCMDQi_CONS_INDX_BASE_DRAM_L_(i) \ + REG32(VCMDQ##i##_CONS_INDX_BASE_DRAM_L, \ + CMDQV_VCMDQ_PAGE1_BASE + 0x8 + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VCMDQ##i##_CONS_INDX_BASE_DRAM_L, ADDR, 0, 32) + +#define SMMU_CMDQV_VCMDQi_CONS_INDX_BASE_DRAM_H_(i) \ + REG32(VCMDQ##i##_CONS_INDX_BASE_DRAM_H, \ + CMDQV_VCMDQ_PAGE1_BASE + 0xc + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VCMDQ##i##_CONS_INDX_BASE_DRAM_H, ADDR, 0, 16) + +/* Page 1 layout: VCMDQ0 */ +SMMU_CMDQV_VCMDQi_BASE_L_(0) +SMMU_CMDQV_VCMDQi_BASE_H_(0) +SMMU_CMDQV_VCMDQi_CONS_INDX_BASE_DRAM_L_(0) +SMMU_CMDQV_VCMDQi_CONS_INDX_BASE_DRAM_H_(0) + +/* Page 1 layout: VCMDQ1 */ +SMMU_CMDQV_VCMDQi_BASE_L_(1) +SMMU_CMDQV_VCMDQi_BASE_H_(1) +SMMU_CMDQV_VCMDQi_CONS_INDX_BASE_DRAM_L_(1) +SMMU_CMDQV_VCMDQi_CONS_INDX_BASE_DRAM_H_(1) + +/* + * VINTF0 logical VCMDQ aperture register windows. + * + * Page 0 @ CMDQV_VINTF_PAGE0_BASE: VCMDQ control and status registers. + * Page 1 @ CMDQV_VINTF_PAGE1_BASE: VCMDQ base and DRAM address registers. + * + * VCMDQs mapped via VINTF are accessed through this aperture as + * hardware aliases of the direct VCMDQ aperture above. + */ + +/* --- Page 0 register macros --- */ +#define SMMU_CMDQV_VI_VCMDQi_CONS_INDX_(i) \ + REG32(VI_VCMDQ##i##_CONS_INDX, \ + CMDQV_VINTF_PAGE0_BASE + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VI_VCMDQ##i##_CONS_INDX, RD, 0, 20) \ + FIELD(VI_VCMDQ##i##_CONS_INDX, ERR, 24, 7) + +#define SMMU_CMDQV_VI_VCMDQi_PROD_INDX_(i) \ + REG32(VI_VCMDQ##i##_PROD_INDX, \ + CMDQV_VINTF_PAGE0_BASE + 0x4 + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VI_VCMDQ##i##_PROD_INDX, WR, 0, 20) + +#define SMMU_CMDQV_VI_VCMDQi_CONFIG_(i) \ + REG32(VI_VCMDQ##i##_CONFIG, \ + CMDQV_VINTF_PAGE0_BASE + 0x8 + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VI_VCMDQ##i##_CONFIG, CMDQ_EN, 0, 1) + +#define SMMU_CMDQV_VI_VCMDQi_STATUS_(i) \ + REG32(VI_VCMDQ##i##_STATUS, \ + CMDQV_VINTF_PAGE0_BASE + 0xc + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VI_VCMDQ##i##_STATUS, CMDQ_EN_OK, 0, 1) + +#define SMMU_CMDQV_VI_VCMDQi_GERROR_(i) \ + REG32(VI_VCMDQ##i##_GERROR, \ + CMDQV_VINTF_PAGE0_BASE + 0x10 + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VI_VCMDQ##i##_GERROR, CMDQ_ERR, 0, 1) \ + FIELD(VI_VCMDQ##i##_GERROR, CONS_DRAM_WR_ABT_ERR, 1, 1) \ + FIELD(VI_VCMDQ##i##_GERROR, CMDQ_INIT_ERR, 2, 1) + +#define SMMU_CMDQV_VI_VCMDQi_GERRORN_(i) \ + REG32(VI_VCMDQ##i##_GERRORN, \ + CMDQV_VINTF_PAGE0_BASE + 0x14 + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VI_VCMDQ##i##_GERRORN, CMDQ_ERR, 0, 1) \ + FIELD(VI_VCMDQ##i##_GERRORN, CONS_DRAM_WR_ABT_ERR, 1, 1) \ + FIELD(VI_VCMDQ##i##_GERRORN, CMDQ_INIT_ERR, 2, 1) + +/* Page 0 layout: VCMDQ0 */ +SMMU_CMDQV_VI_VCMDQi_CONS_INDX_(0) +SMMU_CMDQV_VI_VCMDQi_PROD_INDX_(0) +SMMU_CMDQV_VI_VCMDQi_CONFIG_(0) +SMMU_CMDQV_VI_VCMDQi_STATUS_(0) +SMMU_CMDQV_VI_VCMDQi_GERROR_(0) +SMMU_CMDQV_VI_VCMDQi_GERRORN_(0) + +/* Page 0 layout: VCMDQ1 */ +SMMU_CMDQV_VI_VCMDQi_CONS_INDX_(1) +SMMU_CMDQV_VI_VCMDQi_PROD_INDX_(1) +SMMU_CMDQV_VI_VCMDQi_CONFIG_(1) +SMMU_CMDQV_VI_VCMDQi_STATUS_(1) +SMMU_CMDQV_VI_VCMDQi_GERROR_(1) +SMMU_CMDQV_VI_VCMDQi_GERRORN_(1) + +/* --- Page 1 register macros --- */ +#define SMMU_CMDQV_VI_VCMDQi_BASE_L_(i) \ + REG32(VI_VCMDQ##i##_BASE_L, \ + CMDQV_VINTF_PAGE1_BASE + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VI_VCMDQ##i##_BASE_L, LOG2SIZE, 0, 5) \ + FIELD(VI_VCMDQ##i##_BASE_L, ADDR, 5, 27) + +#define SMMU_CMDQV_VI_VCMDQi_BASE_H_(i) \ + REG32(VI_VCMDQ##i##_BASE_H, \ + CMDQV_VINTF_PAGE1_BASE + 0x4 + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VI_VCMDQ##i##_BASE_H, ADDR, 0, 16) + +#define SMMU_CMDQV_VI_VCMDQi_CONS_INDX_BASE_DRAM_L_(i) \ + REG32(VI_VCMDQ##i##_CONS_INDX_BASE_DRAM_L, \ + CMDQV_VINTF_PAGE1_BASE + 0x8 + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VI_VCMDQ##i##_CONS_INDX_BASE_DRAM_L, ADDR, 0, 32) + +#define SMMU_CMDQV_VI_VCMDQi_CONS_INDX_BASE_DRAM_H_(i) \ + REG32(VI_VCMDQ##i##_CONS_INDX_BASE_DRAM_H, \ + CMDQV_VINTF_PAGE1_BASE + 0xc + i * CMDQV_VCMDQ_STRIDE) \ + FIELD(VI_VCMDQ##i##_CONS_INDX_BASE_DRAM_H, ADDR, 0, 16) + +/* Page 1 layout: VCMDQ0 */ +SMMU_CMDQV_VI_VCMDQi_BASE_L_(0) +SMMU_CMDQV_VI_VCMDQi_BASE_H_(0) +SMMU_CMDQV_VI_VCMDQi_CONS_INDX_BASE_DRAM_L_(0) +SMMU_CMDQV_VI_VCMDQi_CONS_INDX_BASE_DRAM_H_(0) + +/* Page 1 layout: VCMDQ1 */ +SMMU_CMDQV_VI_VCMDQi_BASE_L_(1) +SMMU_CMDQV_VI_VCMDQi_BASE_H_(1) +SMMU_CMDQV_VI_VCMDQi_CONS_INDX_BASE_DRAM_L_(1) +SMMU_CMDQV_VI_VCMDQi_CONS_INDX_BASE_DRAM_H_(1) + const SMMUv3AccelCmdqvOps *tegra241_cmdqv_get_ops(void); #endif /* HW_ARM_TEGRA241_CMDQV_H */ diff --git a/hw/arm/trace-events b/hw/arm/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -XXX,XX +XXX,XX @@ smmuv3_accel_install_ste(uint32_t vsid, const char * type, uint32_t hwpt_id) "vS # tegra241-cmdqv tegra241_cmdqv_read_mmio(uint64_t offset, uint64_t val, unsigned size) "offset: 0x%"PRIx64" val: 0x%"PRIx64" size: 0x%x" tegra241_cmdqv_write_mmio(uint64_t offset, uint64_t val, unsigned size) "offset: 0x%"PRIx64" val: 0x%"PRIx64" size: 0x%x" +tegra241_cmdqv_read_vcmdq_page0(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64 +tegra241_cmdqv_read_vcmdq_page1(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64 # strongarm.c strongarm_uart_update_parameters(const char *label, int speed, char parity, int data_bits, int stop_bits) "%s speed=%d parity=%c data=%d stop=%d" -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> This is the write side counterpart of the VCMDQ read emulation. Add write handling for both the direct VCMDQ aperture and the VINTF logical aperture using the same index decoding and VINTF-to-VCMDQ translation logic as the read path. VINTF aperture writes are translated to their direct-aperture equivalent and update the same cached state. Page 1 registers (BASE, CONS_INDX_BASE) always update the cache. Per the CMDQV architecture, a VCMDQ must be allocated to a Virtual Interface before it is used to send commands to the SMMU. Until that allocation happens, MMIO writes only update cached register state - no command consumption, error handling, or interrupt activity is driven from these writes. Subsequent patches wire up IOMMU_HW_QUEUE_ALLOC, mmap the host VINTF Page 0, and install it into guest MMIO; after that, Page 0 writes from either aperture reach the hardware-backed mmap'd page instead of just the cache. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Message-id: 20260609112552.378999-18-skolothumtho@nvidia.com Co-developed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/tegra241-cmdqv.c | 159 +++++++++++++++++++++++++++++++++++++++- hw/arm/trace-events | 2 + 2 files changed, 159 insertions(+), 2 deletions(-) diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.c +++ b/hw/arm/tegra241-cmdqv.c @@ -XXX,XX +XXX,XX @@ static uint64_t tegra241_cmdqv_config_vintf_read(Tegra241CMDQV *cmdqv, } } +/* + * Write a VCMDQ Page 0 register (control/status) using VCMDQ0_* offsets. + * + * The caller normalizes the MMIO offset such that @offset0 always refers + * to a VCMDQ0_* register, while @index selects the VCMDQ instance. + * + * Page 0 registers are all 32-bit; this helper is only called for 4-byte + * writes. + */ +static void tegra241_cmdqv_write_vcmdq_page0(Tegra241CMDQV *cmdqv, + hwaddr offset0, int index, + uint32_t value, bool direct) +{ + switch (offset0) { + case A_VCMDQ0_CONS_INDX: + cmdqv->vcmdq_cons_indx[index] = value; + break; + case A_VCMDQ0_PROD_INDX: + /* VCMDQ is functional only once allocated to a VINTF; cache only. */ + cmdqv->vcmdq_prod_indx[index] = value; + break; + case A_VCMDQ0_CONFIG: + if (value & R_VCMDQ0_CONFIG_CMDQ_EN_MASK) { + cmdqv->vcmdq_status[index] |= R_VCMDQ0_STATUS_CMDQ_EN_OK_MASK; + } else { + cmdqv->vcmdq_status[index] &= ~R_VCMDQ0_STATUS_CMDQ_EN_OK_MASK; + } + cmdqv->vcmdq_config[index] = value; + break; + case A_VCMDQ0_GERRORN: + /* VCMDQ is functional only once allocated to a VINTF; cache only. */ + cmdqv->vcmdq_gerrorn[index] = value; + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s unhandled write access at 0x%" PRIx64 "\n", + __func__, offset0); + } + trace_tegra241_cmdqv_write_vcmdq_page0(index, direct ? "direct" : "vi", + offset0, value); +} + +/* + * Write a VCMDQ Page 1 register (base / DRAM address) - 4-byte access. + */ +static void tegra241_cmdqv_write_vcmdq_page1(Tegra241CMDQV *cmdqv, + hwaddr offset0, int index, + uint32_t value, bool direct) +{ + switch (offset0) { + case A_VCMDQ0_BASE_L: + cmdqv->vcmdq_base[index] = + deposit64(cmdqv->vcmdq_base[index], 0, 32, value); + break; + case A_VCMDQ0_BASE_H: + cmdqv->vcmdq_base[index] = + deposit64(cmdqv->vcmdq_base[index], 32, 32, value); + break; + case A_VCMDQ0_CONS_INDX_BASE_DRAM_L: + cmdqv->vcmdq_cons_indx_base[index] = + deposit64(cmdqv->vcmdq_cons_indx_base[index], 0, 32, value); + break; + case A_VCMDQ0_CONS_INDX_BASE_DRAM_H: + cmdqv->vcmdq_cons_indx_base[index] = + deposit64(cmdqv->vcmdq_cons_indx_base[index], 32, 32, value); + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s unhandled write access at 0x%" PRIx64 "\n", + __func__, offset0); + } + trace_tegra241_cmdqv_write_vcmdq_page1(index, direct ? "direct" : "vi", + offset0, value); +} + +/* + * Write a VCMDQ Page 1 register - 8-byte access at BASE_L or DRAM_L. + */ +static void tegra241_cmdqv_write_vcmdq_page1_64(Tegra241CMDQV *cmdqv, + hwaddr offset0, int index, + uint64_t value, bool direct) +{ + switch (offset0) { + case A_VCMDQ0_BASE_L: + cmdqv->vcmdq_base[index] = value; + break; + case A_VCMDQ0_CONS_INDX_BASE_DRAM_L: + cmdqv->vcmdq_cons_indx_base[index] = value; + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s unhandled 64-bit write at 0x%" PRIx64 "\n", + __func__, offset0); + } + trace_tegra241_cmdqv_write_vcmdq_page1(index, direct ? "direct" : "vi", + offset0, value); +} + static void tegra241_cmdqv_config_vintf_write(Tegra241CMDQV *cmdqv, hwaddr offset, uint64_t value) { @@ -XXX,XX +XXX,XX @@ out: static void tegra241_cmdqv_writel_mmio(Tegra241CMDQV *cmdqv, hwaddr offset, uint32_t value) { + int index; + switch (offset) { case A_CONFIG: cmdqv->config = value; @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_writel_mmio(Tegra241CMDQV *cmdqv, hwaddr offset, case A_VINTF0_CONFIG ... A_VINTF0_LVCMDQ_ERR_MAP_3: tegra241_cmdqv_config_vintf_write(cmdqv, offset, value); break; + case A_VI_VCMDQ0_CONS_INDX ... A_VI_VCMDQ1_GERRORN: + /* + * VINTF Page0 registers are hardware aliases of VCMDQ Page0 registers. + * Translate the VINTF aperture offset to its VCMDQ Page0 equivalent + * before dispatching to the Page 0 helper. + */ + offset -= CMDQV_VINTF_PAGE0_BASE - CMDQV_VCMDQ_PAGE0_BASE; + index = (offset - CMDQV_VCMDQ_PAGE0_BASE) / CMDQV_VCMDQ_STRIDE; + tegra241_cmdqv_write_vcmdq_page0(cmdqv, + offset - index * CMDQV_VCMDQ_STRIDE, index, value, false); + break; + case A_VCMDQ0_CONS_INDX ... A_VCMDQ1_GERRORN: + /* + * Decode a per-VCMDQ Page 0 access. Each VCMDQ occupies a + * CMDQV_VCMDQ_STRIDE-byte window; extract the index and normalize + * to the VCMDQ0_* offset before calling the Page 0 helper. + */ + index = (offset - CMDQV_VCMDQ_PAGE0_BASE) / CMDQV_VCMDQ_STRIDE; + tegra241_cmdqv_write_vcmdq_page0(cmdqv, + offset - index * CMDQV_VCMDQ_STRIDE, index, value, true); + break; + case A_VI_VCMDQ0_BASE_L ... A_VI_VCMDQ1_CONS_INDX_BASE_DRAM_H: + /* Same VINTF-to-VCMDQ translation as VINTF Page0 case above. */ + offset -= CMDQV_VINTF_PAGE1_BASE - CMDQV_VCMDQ_PAGE1_BASE; + index = (offset - CMDQV_VCMDQ_PAGE1_BASE) / CMDQV_VCMDQ_STRIDE; + tegra241_cmdqv_write_vcmdq_page1(cmdqv, + offset - index * CMDQV_VCMDQ_STRIDE, index, value, false); + break; + case A_VCMDQ0_BASE_L ... A_VCMDQ1_CONS_INDX_BASE_DRAM_H: + index = (offset - CMDQV_VCMDQ_PAGE1_BASE) / CMDQV_VCMDQ_STRIDE; + tegra241_cmdqv_write_vcmdq_page1(cmdqv, + offset - index * CMDQV_VCMDQ_STRIDE, index, value, true); + break; default: qemu_log_mask(LOG_UNIMP, "%s unhandled write access at 0x%" PRIx64 "\n", __func__, offset); @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_writel_mmio(Tegra241CMDQV *cmdqv, hwaddr offset, } /* - * 8-byte MMIO write handler. + * 8-byte MMIO write handler. Only Page 1 BASE / CONS_INDX_BASE_DRAM accept + * full 64-bit writes; other offsets are write-ignored. */ static void tegra241_cmdqv_writell_mmio(Tegra241CMDQV *cmdqv, hwaddr offset, uint64_t value) { - qemu_log_mask(LOG_UNIMP, + int index; + + switch (offset) { + case A_VI_VCMDQ0_BASE_L ... A_VI_VCMDQ1_CONS_INDX_BASE_DRAM_H: + /* + * VINTF Page1 registers are hardware aliases of VCMDQ Page1 registers. + * Translate the VINTF aperture offset to its VCMDQ Page1 equivalent + * before dispatching to the Page 1 helper. + */ + offset -= CMDQV_VINTF_PAGE1_BASE - CMDQV_VCMDQ_PAGE1_BASE; + index = (offset - CMDQV_VCMDQ_PAGE1_BASE) / CMDQV_VCMDQ_STRIDE; + tegra241_cmdqv_write_vcmdq_page1_64(cmdqv, + offset - index * CMDQV_VCMDQ_STRIDE, index, value, false); + break; + case A_VCMDQ0_BASE_L ... A_VCMDQ1_CONS_INDX_BASE_DRAM_H: + index = (offset - CMDQV_VCMDQ_PAGE1_BASE) / CMDQV_VCMDQ_STRIDE; + tegra241_cmdqv_write_vcmdq_page1_64(cmdqv, + offset - index * CMDQV_VCMDQ_STRIDE, index, value, true); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s unhandled 64-bit write at 0x%" PRIx64 " (WI)\n", __func__, offset); + } } static void tegra241_cmdqv_write_mmio(void *opaque, hwaddr offset, diff --git a/hw/arm/trace-events b/hw/arm/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -XXX,XX +XXX,XX @@ tegra241_cmdqv_read_mmio(uint64_t offset, uint64_t val, unsigned size) "offset: tegra241_cmdqv_write_mmio(uint64_t offset, uint64_t val, unsigned size) "offset: 0x%"PRIx64" val: 0x%"PRIx64" size: 0x%x" tegra241_cmdqv_read_vcmdq_page0(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64 tegra241_cmdqv_read_vcmdq_page1(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64 +tegra241_cmdqv_write_vcmdq_page0(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64 +tegra241_cmdqv_write_vcmdq_page1(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64 # strongarm.c strongarm_uart_update_parameters(const char *label, int speed, char parity, int data_bits, int stop_bits) "%s speed=%d parity=%c data=%d stop=%d" -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> Add support for allocating IOMMUFD hardware queues when the guest programs the VCMDQ BASE registers. VCMDQ_EN lives in VCMDQ_CONFIG, which is on the VINTF Page0 region that a later patch installs into guest MMIO — so QEMU won't trap its writes. Allocate the hardware queue instead once all of these are set: BASE programmed, CMDQ_ALLOC_MAP.ALLOC, and CMDQV / VINTF enabled. Each precondition write retries the allocation, so the guest may program them in any order. iommufd_backend_alloc_hw_queue() needs the guest physical address of the VCMDQ ring buffer, so allocation is deferred until the guest has populated BASE. If a hardware queue was previously allocated for the same VCMDQ, free it before reallocation. All allocated VCMDQs are freed when CMDQV or VINTF is disabled, when the ALLOC bit is cleared, or on reset. On allocation failure, set CMDQ_INIT_ERR and clear CMDQ_EN_OK in the cache so trapped guest reads see the failure rather than a queue that looks live. Clear them on a later successful allocation. A guest CMDQ_EN write then sets CMDQ_EN_OK only if CMDQ_INIT_ERR is clear. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-19-skolothumtho@nvidia.com Co-developed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/tegra241-cmdqv.c | 171 +++++++++++++++++++++++++++++++++++++--- hw/arm/tegra241-cmdqv.h | 11 +++ 2 files changed, 171 insertions(+), 11 deletions(-) diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.c +++ b/hw/arm/tegra241-cmdqv.c @@ -XXX,XX +XXX,XX @@ #include "tegra241-cmdqv.h" #include "trace.h" +static void tegra241_cmdqv_free_vcmdq(Tegra241CMDQV *cmdqv, int index) +{ + IOMMUFDViommu *viommu = cmdqv->s_accel->viommu; + IOMMUFDHWqueue *vcmdq = cmdqv->vcmdq[index]; + + if (!vcmdq) { + return; + } + iommufd_backend_free_id(viommu->iommufd, vcmdq->hw_queue_id); + g_free(vcmdq); + cmdqv->vcmdq[index] = NULL; +} + +/* + * A VCMDQ's HW queue can be allocated once the guest has programmed: + * - VCMDQ_BASE (ring buffer GPA and size). This only checks that BASE is + * non-zero, not that both the _L and _H halves have been written; a + * half-written BASE may pass here, but the write of the second half + * re-runs setup and reallocates with the complete address. + * - the VINTF mapping (CMDQ_ALLOC_MAP.ALLOC). + * - both the CMDQV global enable and the VINTF enable. + */ +static bool tegra241_cmdqv_vcmdq_ready_to_alloc(Tegra241CMDQV *cmdqv, int index) +{ + return cmdqv->vcmdq_base[index] && + (cmdqv->cmdq_alloc_map[index] & R_CMDQ_ALLOC_MAP_0_ALLOC_MASK) && + tegra241_cmdqv_enabled(cmdqv) && tegra241_vintf_enabled(cmdqv); +} + +/* + * Allocate a host HW VCMDQ from the current cached BASE / size for @index. + * No-op (returns true) until the VCMDQ is ready to be allocated. + */ +static bool tegra241_cmdqv_setup_vcmdq(Tegra241CMDQV *cmdqv, int index, + Error **errp) +{ + SMMUv3AccelState *accel = cmdqv->s_accel; + uint64_t base_mask = (uint64_t)R_VCMDQ0_BASE_L_ADDR_MASK | + (uint64_t)R_VCMDQ0_BASE_H_ADDR_MASK << 32; + uint64_t addr = cmdqv->vcmdq_base[index] & base_mask; + uint64_t log2 = cmdqv->vcmdq_base[index] & R_VCMDQ0_BASE_L_LOG2SIZE_MASK; + uint64_t size = 1ULL << (log2 + 4); + IOMMUFDViommu *viommu = accel->viommu; + IOMMUFDHWqueue *hw_queue; + uint32_t hw_queue_id; + + if (!tegra241_cmdqv_vcmdq_ready_to_alloc(cmdqv, index)) { + return true; + } + + tegra241_cmdqv_free_vcmdq(cmdqv, index); + + if (!iommufd_backend_alloc_hw_queue(viommu->iommufd, viommu->viommu_id, + IOMMU_HW_QUEUE_TYPE_TEGRA241_CMDQV, + index, addr, size, &hw_queue_id, + errp)) { + /* Record the failure in the cache. */ + cmdqv->vcmdq_gerror[index] |= R_VCMDQ0_GERROR_CMDQ_INIT_ERR_MASK; + cmdqv->vcmdq_status[index] &= ~R_VCMDQ0_STATUS_CMDQ_EN_OK_MASK; + return false; + } + hw_queue = g_new(IOMMUFDHWqueue, 1); + hw_queue->hw_queue_id = hw_queue_id; + hw_queue->viommu = viommu; + cmdqv->vcmdq[index] = hw_queue; + + cmdqv->vcmdq_gerror[index] &= ~R_VCMDQ0_GERROR_CMDQ_INIT_ERR_MASK; + cmdqv->vcmdq_status[index] |= R_VCMDQ0_STATUS_CMDQ_EN_OK_MASK; + + return true; +} + +static void tegra241_cmdqv_free_all_vcmdq(Tegra241CMDQV *cmdqv) +{ + /* uapi/linux/iommufd.h: hw_queue destroy must be in descending @index. */ + for (int i = (TEGRA241_CMDQV_MAX_CMDQ - 1); i >= 0; i--) { + tegra241_cmdqv_free_vcmdq(cmdqv, i); + } +} + +static void tegra241_cmdqv_setup_all_vcmdq(Tegra241CMDQV *cmdqv, + Error **errp) +{ + for (int i = 0; i < TEGRA241_CMDQV_MAX_CMDQ; i++) { + if (!tegra241_cmdqv_setup_vcmdq(cmdqv, i, errp)) { + return; + } + } +} + /* * Read a VCMDQ Page 0 register (control/status) using VCMDQ0_* offsets. * @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_write_vcmdq_page0(Tegra241CMDQV *cmdqv, break; case A_VCMDQ0_CONFIG: if (value & R_VCMDQ0_CONFIG_CMDQ_EN_MASK) { - cmdqv->vcmdq_status[index] |= R_VCMDQ0_STATUS_CMDQ_EN_OK_MASK; + /* Report init error if any. */ + if (!(cmdqv->vcmdq_gerror[index] & + R_VCMDQ0_GERROR_CMDQ_INIT_ERR_MASK)) { + cmdqv->vcmdq_status[index] |= + R_VCMDQ0_STATUS_CMDQ_EN_OK_MASK; + } } else { cmdqv->vcmdq_status[index] &= ~R_VCMDQ0_STATUS_CMDQ_EN_OK_MASK; } @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_write_vcmdq_page0(Tegra241CMDQV *cmdqv, */ static void tegra241_cmdqv_write_vcmdq_page1(Tegra241CMDQV *cmdqv, hwaddr offset0, int index, - uint32_t value, bool direct) + uint32_t value, bool direct, + Error **errp) { switch (offset0) { case A_VCMDQ0_BASE_L: cmdqv->vcmdq_base[index] = deposit64(cmdqv->vcmdq_base[index], 0, 32, value); + tegra241_cmdqv_setup_vcmdq(cmdqv, index, errp); break; case A_VCMDQ0_BASE_H: cmdqv->vcmdq_base[index] = deposit64(cmdqv->vcmdq_base[index], 32, 32, value); + tegra241_cmdqv_setup_vcmdq(cmdqv, index, errp); break; case A_VCMDQ0_CONS_INDX_BASE_DRAM_L: cmdqv->vcmdq_cons_indx_base[index] = @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_write_vcmdq_page1(Tegra241CMDQV *cmdqv, */ static void tegra241_cmdqv_write_vcmdq_page1_64(Tegra241CMDQV *cmdqv, hwaddr offset0, int index, - uint64_t value, bool direct) + uint64_t value, bool direct, + Error **errp) { switch (offset0) { case A_VCMDQ0_BASE_L: cmdqv->vcmdq_base[index] = value; + tegra241_cmdqv_setup_vcmdq(cmdqv, index, errp); break; case A_VCMDQ0_CONS_INDX_BASE_DRAM_L: cmdqv->vcmdq_cons_indx_base[index] = value; @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_write_vcmdq_page1_64(Tegra241CMDQV *cmdqv, } static void tegra241_cmdqv_config_vintf_write(Tegra241CMDQV *cmdqv, - hwaddr offset, uint64_t value) + hwaddr offset, uint64_t value, + Error **errp) { int i; @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_config_vintf_write(Tegra241CMDQV *cmdqv, cmdqv->vintf_config = value; if (value & R_VINTF0_CONFIG_ENABLE_MASK) { cmdqv->vintf_status |= R_VINTF0_STATUS_ENABLE_OK_MASK; + /* + * VCMDQs whose BASE was programmed before VINTF was + * enabled need their hw_queue allocated now. + */ + tegra241_cmdqv_setup_all_vcmdq(cmdqv, errp); } else { + tegra241_cmdqv_free_all_vcmdq(cmdqv); cmdqv->vintf_status &= ~R_VINTF0_STATUS_ENABLE_OK_MASK; } break; @@ -XXX,XX +XXX,XX @@ out: static void tegra241_cmdqv_writel_mmio(Tegra241CMDQV *cmdqv, hwaddr offset, uint32_t value) { + Error *local_err = NULL; int index; switch (offset) { @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_writel_mmio(Tegra241CMDQV *cmdqv, hwaddr offset, cmdqv->config = value; if (value & R_CONFIG_CMDQV_EN_MASK) { cmdqv->status |= R_STATUS_CMDQV_ENABLED_MASK; + /* + * VCMDQs whose BASE was programmed before CMDQV was enabled + * need their hw_queue allocated now. + */ + tegra241_cmdqv_setup_all_vcmdq(cmdqv, &local_err); } else { + tegra241_cmdqv_free_all_vcmdq(cmdqv); cmdqv->status &= ~R_STATUS_CMDQV_ENABLED_MASK; } break; case A_VI_INT_MASK_0 ... A_VI_INT_MASK_1: cmdqv->vi_int_mask[(offset - A_VI_INT_MASK_0) / 4] = value; break; - case A_CMDQ_ALLOC_MAP_0 ... A_CMDQ_ALLOC_MAP_1: - cmdqv->cmdq_alloc_map[(offset - A_CMDQ_ALLOC_MAP_0) / 4] = value; + case A_CMDQ_ALLOC_MAP_0 ... A_CMDQ_ALLOC_MAP_1: { + int idx = (offset - A_CMDQ_ALLOC_MAP_0) / 4; + bool was_alloc = cmdqv->cmdq_alloc_map[idx] & + R_CMDQ_ALLOC_MAP_0_ALLOC_MASK; + bool now_alloc = value & R_CMDQ_ALLOC_MAP_0_ALLOC_MASK; + + cmdqv->cmdq_alloc_map[idx] = value; + /* + * If the VCMDQ was already programmed (BASE) before mapping, fire + * setup on the ALLOC 0->1 transition; tear down on 1->0. + */ + if (!was_alloc && now_alloc) { + tegra241_cmdqv_setup_vcmdq(cmdqv, idx, &local_err); + } else if (was_alloc && !now_alloc) { + tegra241_cmdqv_free_vcmdq(cmdqv, idx); + } break; + } case A_VINTF0_CONFIG ... A_VINTF0_LVCMDQ_ERR_MAP_3: - tegra241_cmdqv_config_vintf_write(cmdqv, offset, value); + tegra241_cmdqv_config_vintf_write(cmdqv, offset, value, &local_err); break; case A_VI_VCMDQ0_CONS_INDX ... A_VI_VCMDQ1_GERRORN: /* @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_writel_mmio(Tegra241CMDQV *cmdqv, hwaddr offset, offset -= CMDQV_VINTF_PAGE1_BASE - CMDQV_VCMDQ_PAGE1_BASE; index = (offset - CMDQV_VCMDQ_PAGE1_BASE) / CMDQV_VCMDQ_STRIDE; tegra241_cmdqv_write_vcmdq_page1(cmdqv, - offset - index * CMDQV_VCMDQ_STRIDE, index, value, false); + offset - index * CMDQV_VCMDQ_STRIDE, index, value, false, + &local_err); break; case A_VCMDQ0_BASE_L ... A_VCMDQ1_CONS_INDX_BASE_DRAM_H: index = (offset - CMDQV_VCMDQ_PAGE1_BASE) / CMDQV_VCMDQ_STRIDE; tegra241_cmdqv_write_vcmdq_page1(cmdqv, - offset - index * CMDQV_VCMDQ_STRIDE, index, value, true); + offset - index * CMDQV_VCMDQ_STRIDE, index, value, true, + &local_err); break; default: qemu_log_mask(LOG_UNIMP, "%s unhandled write access at 0x%" PRIx64 "\n", __func__, offset); } + + if (local_err) { + error_report_err(local_err); + } } /* @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_writel_mmio(Tegra241CMDQV *cmdqv, hwaddr offset, static void tegra241_cmdqv_writell_mmio(Tegra241CMDQV *cmdqv, hwaddr offset, uint64_t value) { + Error *local_err = NULL; int index; switch (offset) { @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_writell_mmio(Tegra241CMDQV *cmdqv, hwaddr offset, offset -= CMDQV_VINTF_PAGE1_BASE - CMDQV_VCMDQ_PAGE1_BASE; index = (offset - CMDQV_VCMDQ_PAGE1_BASE) / CMDQV_VCMDQ_STRIDE; tegra241_cmdqv_write_vcmdq_page1_64(cmdqv, - offset - index * CMDQV_VCMDQ_STRIDE, index, value, false); + offset - index * CMDQV_VCMDQ_STRIDE, index, value, false, + &local_err); break; case A_VCMDQ0_BASE_L ... A_VCMDQ1_CONS_INDX_BASE_DRAM_H: index = (offset - CMDQV_VCMDQ_PAGE1_BASE) / CMDQV_VCMDQ_STRIDE; tegra241_cmdqv_write_vcmdq_page1_64(cmdqv, - offset - index * CMDQV_VCMDQ_STRIDE, index, value, true); + offset - index * CMDQV_VCMDQ_STRIDE, index, value, true, + &local_err); break; default: qemu_log_mask(LOG_UNIMP, "%s unhandled 64-bit write at 0x%" PRIx64 " (WI)\n", __func__, offset); } + + if (local_err) { + error_report_err(local_err); + } } static void tegra241_cmdqv_write_mmio(void *opaque, hwaddr offset, @@ -XXX,XX +XXX,XX @@ free_viommu: static void tegra241_cmdqv_reset(SMMUv3State *s) { + Tegra241CMDQV *cmdqv = s->s_accel->cmdqv; + + if (!cmdqv) { + return; + } + + tegra241_cmdqv_free_all_vcmdq(cmdqv); } static const MemoryRegionOps mmio_cmdqv_ops = { diff --git a/hw/arm/tegra241-cmdqv.h b/hw/arm/tegra241-cmdqv.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.h +++ b/hw/arm/tegra241-cmdqv.h @@ -XXX,XX +XXX,XX @@ typedef struct Tegra241CMDQV { MemoryRegion mmio_cmdqv; qemu_irq irq; IOMMUFDVeventq *veventq; + IOMMUFDHWqueue *vcmdq[TEGRA241_CMDQV_MAX_CMDQ]; void *vintf_page0; /* CMDQ-V Config page register cache */ @@ -XXX,XX +XXX,XX @@ SMMU_CMDQV_VI_VCMDQi_BASE_H_(1) SMMU_CMDQV_VI_VCMDQi_CONS_INDX_BASE_DRAM_L_(1) SMMU_CMDQV_VI_VCMDQi_CONS_INDX_BASE_DRAM_H_(1) +static inline bool tegra241_cmdqv_enabled(Tegra241CMDQV *cmdqv) +{ + return cmdqv->status & R_STATUS_CMDQV_ENABLED_MASK; +} + +static inline bool tegra241_vintf_enabled(Tegra241CMDQV *cmdqv) +{ + return cmdqv->vintf_status & R_VINTF0_STATUS_ENABLE_OK_MASK; +} + const SMMUv3AccelCmdqvOps *tegra241_cmdqv_get_ops(void); #endif /* HW_ARM_TEGRA241_CMDQV_H */ -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> Introduce tegra241_cmdqv_vintf_lvcmdq_ptr() to route VCMDQ Page 0 register accesses through the mmap'd host VINTF Page 0 backing once a hardware queue has been allocated for the VCMDQ. The two QEMU-trapped Page 0 apertures (direct at 0x10000, VINTF at 0x30000) are hardware aliases of the same underlying registers. A subsequent patch installs the VINTF aperture as a RAM-device into guest MMIO; in this patch both remain QEMU-trapped. The direct VCMDQ aperture stays QEMU-trapped (rather than aliased to the VINTF mmap) so that writes to an unallocated VCMDQ remain well-defined. The CMDQV architecture allows software to program a VCMDQ through the direct aperture without first allocating it to a VINTF; aliasing to the VINTF mmap would route those writes into unallocated logical slots where the hardware silently drops them. A VCMDQ Page 0 access is served from one of two sources: - Cache-backed: no hw_queue is allocated for the VCMDQ (HW_QUEUE_ALLOC has not yet succeeded). Both apertures use QEMU's register cache. - HW-backed: HW_QUEUE_ALLOC has succeeded. Both apertures access the registers directly through the mmap'd host VINTF Page 0. tegra241_cmdqv_sync_vcmdq() copies any cached writes (CONS_INDX, PROD_INDX, CONFIG, GERRORN) into the mmap'd page on the cache-to-HW transition so the guest's earlier register state survives. Freeing a VCMDQ clears the cached Page0 registers. Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Message-id: 20260609112552.378999-20-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/tegra241-cmdqv.c | 89 +++++++++++++++++++++++++++++++++++++++++ hw/arm/trace-events | 4 +- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.c +++ b/hw/arm/tegra241-cmdqv.c @@ -XXX,XX +XXX,XX @@ #include "tegra241-cmdqv.h" #include "trace.h" +static void tegra241_cmdqv_reset_vcmdq_cache(Tegra241CMDQV *cmdqv, int index) +{ + cmdqv->vcmdq_cons_indx[index] = 0; + cmdqv->vcmdq_prod_indx[index] = 0; + cmdqv->vcmdq_config[index] = 0; + cmdqv->vcmdq_status[index] = 0; + cmdqv->vcmdq_gerror[index] = 0; + cmdqv->vcmdq_gerrorn[index] = 0; +} + static void tegra241_cmdqv_free_vcmdq(Tegra241CMDQV *cmdqv, int index) { IOMMUFDViommu *viommu = cmdqv->s_accel->viommu; @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_free_vcmdq(Tegra241CMDQV *cmdqv, int index) iommufd_backend_free_id(viommu->iommufd, vcmdq->hw_queue_id); g_free(vcmdq); cmdqv->vcmdq[index] = NULL; + tegra241_cmdqv_reset_vcmdq_cache(cmdqv, index); } /* @@ -XXX,XX +XXX,XX @@ static bool tegra241_cmdqv_vcmdq_ready_to_alloc(Tegra241CMDQV *cmdqv, int index) tegra241_cmdqv_enabled(cmdqv) && tegra241_vintf_enabled(cmdqv); } +/* + * Return a pointer into the mmap'd VINTF page0 for the VCMDQ Page 0 + * register at @offset0 in VCMDQ slot @index, or NULL when the VCMDQ + * has no hw_queue allocated or the host VINTF page0 is not mmap'd. + */ +static inline uint32_t *tegra241_cmdqv_vintf_lvcmdq_ptr(Tegra241CMDQV *cmdqv, + int index, hwaddr offset0) +{ + if (!cmdqv->vcmdq[index] || !cmdqv->vintf_page0) { + return NULL; + } + return (uint32_t *)(cmdqv->vintf_page0 + + (index * CMDQV_VCMDQ_STRIDE) + + (offset0 - CMDQV_VCMDQ_PAGE0_BASE)); +} + +/* + * Flush cached register writes into the mmap'd host VINTF page0 after a + * successful HW_QUEUE_ALLOC, so the guest's earlier writes survive + * the cache-to-hardware transition. + */ +static void tegra241_cmdqv_sync_vcmdq(Tegra241CMDQV *cmdqv, int index) +{ + uint32_t *ptr; + + ptr = tegra241_cmdqv_vintf_lvcmdq_ptr(cmdqv, index, A_VCMDQ0_CONS_INDX); + if (!ptr) { + return; + } + *ptr = cmdqv->vcmdq_cons_indx[index]; + + ptr = tegra241_cmdqv_vintf_lvcmdq_ptr(cmdqv, index, A_VCMDQ0_PROD_INDX); + *ptr = cmdqv->vcmdq_prod_indx[index]; + + ptr = tegra241_cmdqv_vintf_lvcmdq_ptr(cmdqv, index, A_VCMDQ0_CONFIG); + *ptr = cmdqv->vcmdq_config[index]; + + ptr = tegra241_cmdqv_vintf_lvcmdq_ptr(cmdqv, index, A_VCMDQ0_GERRORN); + *ptr = cmdqv->vcmdq_gerrorn[index]; +} + /* * Allocate a host HW VCMDQ from the current cached BASE / size for @index. * No-op (returns true) until the VCMDQ is ready to be allocated. @@ -XXX,XX +XXX,XX @@ static bool tegra241_cmdqv_setup_vcmdq(Tegra241CMDQV *cmdqv, int index, cmdqv->vcmdq_gerror[index] &= ~R_VCMDQ0_GERROR_CMDQ_INIT_ERR_MASK; cmdqv->vcmdq_status[index] |= R_VCMDQ0_STATUS_CMDQ_EN_OK_MASK; + /* Push cached writes to HW; freeing resets the cache. */ + tegra241_cmdqv_sync_vcmdq(cmdqv, index); + return true; } @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_setup_all_vcmdq(Tegra241CMDQV *cmdqv, * * The caller normalizes the MMIO offset such that @offset0 always refers * to a VCMDQ0_* register, while @index selects the VCMDQ instance. + * + * If the VCMDQ is allocated and the host VINTF page0 is mmap'd, read + * directly from the host VINTF page0 backing. Otherwise, fall back to + * the cache. */ static uint64_t tegra241_cmdqv_read_vcmdq_page0(Tegra241CMDQV *cmdqv, hwaddr offset0, int index, bool direct) { + uint32_t *ptr = tegra241_cmdqv_vintf_lvcmdq_ptr(cmdqv, index, offset0); uint64_t val = 0; + if (ptr) { + val = *ptr; + goto out; + } + switch (offset0) { case A_VCMDQ0_CONS_INDX: val = cmdqv->vcmdq_cons_indx[index]; @@ -XXX,XX +XXX,XX @@ static uint64_t tegra241_cmdqv_read_vcmdq_page0(Tegra241CMDQV *cmdqv, "%s unhandled read access at 0x%" PRIx64 "\n", __func__, offset0); } +out: trace_tegra241_cmdqv_read_vcmdq_page0(index, direct ? "direct" : "vi", + ptr ? "hw" : "cache", offset0, val); return val; } @@ -XXX,XX +XXX,XX @@ static uint64_t tegra241_cmdqv_config_vintf_read(Tegra241CMDQV *cmdqv, * * Page 0 registers are all 32-bit; this helper is only called for 4-byte * writes. + * + * If the VCMDQ is allocated and the host VINTF page0 is mmap'd, write + * directly to the VINTF page0 backing. Otherwise, update the cache. */ static void tegra241_cmdqv_write_vcmdq_page0(Tegra241CMDQV *cmdqv, hwaddr offset0, int index, uint32_t value, bool direct) { + uint32_t *ptr = tegra241_cmdqv_vintf_lvcmdq_ptr(cmdqv, index, offset0); + bool hw = false; + + if (ptr) { + switch (offset0) { + case A_VCMDQ0_CONS_INDX: + case A_VCMDQ0_PROD_INDX: + case A_VCMDQ0_CONFIG: + case A_VCMDQ0_GERRORN: + *ptr = value; + hw = true; + goto out; + default: + break; + } + } + switch (offset0) { case A_VCMDQ0_CONS_INDX: cmdqv->vcmdq_cons_indx[index] = value; @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_write_vcmdq_page0(Tegra241CMDQV *cmdqv, "%s unhandled write access at 0x%" PRIx64 "\n", __func__, offset0); } +out: trace_tegra241_cmdqv_write_vcmdq_page0(index, direct ? "direct" : "vi", + hw ? "hw" : "cache", offset0, value); } diff --git a/hw/arm/trace-events b/hw/arm/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -XXX,XX +XXX,XX @@ smmuv3_accel_install_ste(uint32_t vsid, const char * type, uint32_t hwpt_id) "vS # tegra241-cmdqv tegra241_cmdqv_read_mmio(uint64_t offset, uint64_t val, unsigned size) "offset: 0x%"PRIx64" val: 0x%"PRIx64" size: 0x%x" tegra241_cmdqv_write_mmio(uint64_t offset, uint64_t val, unsigned size) "offset: 0x%"PRIx64" val: 0x%"PRIx64" size: 0x%x" -tegra241_cmdqv_read_vcmdq_page0(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64 +tegra241_cmdqv_read_vcmdq_page0(int index, const char *aperture, const char *backing, uint64_t offset0, uint64_t val) "vcmdq[%d] %s (%s) offset0: 0x%"PRIx64" val: 0x%"PRIx64 tegra241_cmdqv_read_vcmdq_page1(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64 -tegra241_cmdqv_write_vcmdq_page0(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64 +tegra241_cmdqv_write_vcmdq_page0(int index, const char *aperture, const char *backing, uint64_t offset0, uint64_t val) "vcmdq[%d] %s (%s) offset0: 0x%"PRIx64" val: 0x%"PRIx64 tegra241_cmdqv_write_vcmdq_page1(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64 # strongarm.c -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> Some RAM device regions created with memory_region_init_ram_device_ptr() are not intended to be P2P DMA targets. The VFIO listener currently treats all RAM device regions as DMA capable and attempts to map them into the IOMMU. For regions without dma-buf backing this fails and prints warnings such as: IOMMU_IOAS_MAP failed: Bad address, PCI BAR? Introduce a MemoryRegion flag (ram_device_skip_iommu_map) to mark RAM device regions that should not be IOMMU mapped, paired with memory_region_skip_iommu_map() / memory_region_set_skip_iommu_map() accessors. When the flag is set, the VFIO listener skips DMA mapping for that region. Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Message-id: 20260609112552.378999-21-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/vfio/listener.c | 6 ++++++ hw/vfio/trace-events | 1 + include/system/memory.h | 21 +++++++++++++++++++++ system/memory.c | 10 ++++++++++ 4 files changed, 38 insertions(+) diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index XXXXXXX..XXXXXXX 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -XXX,XX +XXX,XX @@ void vfio_container_region_add(VFIOContainer *bcontainer, } } + if (memory_region_skip_iommu_map(section->mr)) { + trace_vfio_listener_region_skip_dma_map(memory_region_name(section->mr), + iova, int128_get64(llsize)); + return; + } + ret = vfio_container_dma_map(bcontainer, iova, int128_get64(llsize), vaddr, section->readonly, section->mr); if (ret) { diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -XXX,XX +XXX,XX @@ vfio_listener_region_del_iommu(const char *name) "region_del [iommu] %s" vfio_listener_region_add_ram(uint64_t iova_start, uint64_t iova_end, void *vaddr) "region_add [ram] 0x%"PRIx64" - 0x%"PRIx64" [%p]" vfio_known_safe_misalignment(const char *name, uint64_t iova, uint64_t offset_within_region, uintptr_t page_size) "Region \"%s\" iova=0x%"PRIx64" offset_within_region=0x%"PRIx64" qemu_real_host_page_size=0x%"PRIxPTR vfio_listener_region_add_no_dma_map(const char *name, uint64_t iova, uint64_t size, uint64_t page_size) "Region \"%s\" 0x%"PRIx64" size=0x%"PRIx64" is not aligned to 0x%"PRIx64" and cannot be mapped for DMA" +vfio_listener_region_skip_dma_map(const char *name, uint64_t iova, uint64_t size) "Region \"%s\" 0x%"PRIx64" size=0x%"PRIx64" marked to skip IOMMU mapping" vfio_listener_region_del(uint64_t start, uint64_t end) "region_del 0x%"PRIx64" - 0x%"PRIx64 vfio_device_dirty_tracking_update(uint64_t start, uint64_t end, uint64_t min, uint64_t max) "section 0x%"PRIx64" - 0x%"PRIx64" -> update [0x%"PRIx64" - 0x%"PRIx64"]" vfio_device_dirty_tracking_start(int nr_ranges, uint64_t min32, uint64_t max32, uint64_t min64, uint64_t max64, uint64_t minpci, uint64_t maxpci) "nr_ranges %d 32:[0x%"PRIx64" - 0x%"PRIx64"], 64:[0x%"PRIx64" - 0x%"PRIx64"], pci64:[0x%"PRIx64" - 0x%"PRIx64"]" diff --git a/include/system/memory.h b/include/system/memory.h index XXXXXXX..XXXXXXX 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -XXX,XX +XXX,XX @@ struct MemoryRegion { /* For devices designed to perform re-entrant IO into their own IO MRs */ bool disable_reentrancy_guard; + /* RAM device region that does not require IOMMU mapping for P2P */ + bool ram_device_skip_iommu_map; }; struct IOMMUMemoryRegion { @@ -XXX,XX +XXX,XX @@ static inline bool memory_region_is_romd(const MemoryRegion *mr) */ bool memory_region_is_protected(const MemoryRegion *mr); +/** + * memory_region_skip_iommu_map: check whether a memory region is excluded + * from IOMMU mapping + * + * Returns %true if @mr is a RAM device region marked to skip IOMMU mapping. + * + * @mr: the memory region being queried + */ +bool memory_region_skip_iommu_map(const MemoryRegion *mr); + +/** + * memory_region_set_skip_iommu_map: mark a RAM device region to skip IOMMU + * mapping + * + * @mr: the memory region being modified + * @skip: %true to skip IOMMU mapping, %false to allow it + */ +void memory_region_set_skip_iommu_map(MemoryRegion *mr, bool skip); + /** * memory_region_has_guest_memfd: check whether a memory region has guest_memfd * associated diff --git a/system/memory.c b/system/memory.c index XXXXXXX..XXXXXXX 100644 --- a/system/memory.c +++ b/system/memory.c @@ -XXX,XX +XXX,XX @@ bool memory_region_is_protected(const MemoryRegion *mr) return mr->ram && (mr->ram_block->flags & RAM_PROTECTED); } +bool memory_region_skip_iommu_map(const MemoryRegion *mr) +{ + return memory_region_is_ram_device(mr) && mr->ram_device_skip_iommu_map; +} + +void memory_region_set_skip_iommu_map(MemoryRegion *mr, bool skip) +{ + mr->ram_device_skip_iommu_map = skip; +} + bool memory_region_has_guest_memfd(const MemoryRegion *mr) { return mr->ram_block && mr->ram_block->guest_memfd >= 0; -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> Install the mmap'd host VINTF page0 as a RAM-device MemoryRegion backing the guest's virtual VINTF Page 0 aperture (guest MMIO offset 0x30000) when VINTF is enabled, and remove it on VINTF disable or reset. This eliminates QEMU trapping for hot-path CONS/PROD index updates via that aperture. After this patch, the two VCMDQ Page 0 apertures use different access paths: the direct aperture (0x10000) remains QEMU-trapped, while the VINTF aperture (0x30000) is a guest-direct RAM mapping. The direct aperture is intentionally kept trapped (not aliased to the host VINTF mmap) so that writes to an unallocated VCMDQ remain well-defined. The CMDQV architecture allows software to program a VCMDQ through the direct aperture without first allocating it to a VINTF; aliasing would route those writes to unallocated logical slots in the VINTF page, where the hardware silently drops them. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-22-skolothumtho@nvidia.com Co-developed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/tegra241-cmdqv.c | 37 +++++++++++++++++++++++++++++++++++++ hw/arm/tegra241-cmdqv.h | 1 + 2 files changed, 38 insertions(+) diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.c +++ b/hw/arm/tegra241-cmdqv.c @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_reset_vcmdq_cache(Tegra241CMDQV *cmdqv, int index) cmdqv->vcmdq_gerrorn[index] = 0; } +static void tegra241_cmdqv_guest_unmap_vintf_page0(Tegra241CMDQV *cmdqv) +{ + if (!cmdqv->mr_vintf_page0) { + return; + } + + memory_region_del_subregion(&cmdqv->mmio_cmdqv, cmdqv->mr_vintf_page0); + object_unparent(OBJECT(cmdqv->mr_vintf_page0)); + g_free(cmdqv->mr_vintf_page0); + cmdqv->mr_vintf_page0 = NULL; +} + +static void tegra241_cmdqv_guest_map_vintf_page0(Tegra241CMDQV *cmdqv) +{ + char *name; + + if (cmdqv->mr_vintf_page0) { + return; + } + + name = g_strdup_printf("%s vintf-page0", + memory_region_name(&cmdqv->mmio_cmdqv)); + cmdqv->mr_vintf_page0 = g_malloc0(sizeof(*cmdqv->mr_vintf_page0)); + memory_region_init_ram_device_ptr(cmdqv->mr_vintf_page0, + memory_region_owner(&cmdqv->mmio_cmdqv), + name, VINTF_PAGE_SIZE, + cmdqv->vintf_page0); + memory_region_set_skip_iommu_map(cmdqv->mr_vintf_page0, true); + memory_region_add_subregion_overlap(&cmdqv->mmio_cmdqv, + CMDQV_VINTF_PAGE0_BASE, + cmdqv->mr_vintf_page0, 1); + g_free(name); +} + static void tegra241_cmdqv_free_vcmdq(Tegra241CMDQV *cmdqv, int index) { IOMMUFDViommu *viommu = cmdqv->s_accel->viommu; @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_config_vintf_write(Tegra241CMDQV *cmdqv, * enabled need their hw_queue allocated now. */ tegra241_cmdqv_setup_all_vcmdq(cmdqv, errp); + tegra241_cmdqv_guest_map_vintf_page0(cmdqv); } else { + tegra241_cmdqv_guest_unmap_vintf_page0(cmdqv); tegra241_cmdqv_free_all_vcmdq(cmdqv); cmdqv->vintf_status &= ~R_VINTF0_STATUS_ENABLE_OK_MASK; } @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_reset(SMMUv3State *s) return; } + tegra241_cmdqv_guest_unmap_vintf_page0(cmdqv); tegra241_cmdqv_free_all_vcmdq(cmdqv); } diff --git a/hw/arm/tegra241-cmdqv.h b/hw/arm/tegra241-cmdqv.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.h +++ b/hw/arm/tegra241-cmdqv.h @@ -XXX,XX +XXX,XX @@ typedef struct Tegra241CMDQV { IOMMUFDVeventq *veventq; IOMMUFDHWqueue *vcmdq[TEGRA241_CMDQV_MAX_CMDQ]; void *vintf_page0; + MemoryRegion *mr_vintf_page0; /* CMDQ-V Config page register cache */ uint32_t config; -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> Move the vEVENTQ read and validation logic into a common helper smmuv3_accel_event_read_validate(). The helper performs the read(), checks for overflow and short reads, validates the sequence number, and updates the sequence state. This helper can be reused for Tegra241 CMDQV vEVENTQ support in a subsequent patch. Error handling is slightly adjusted: instead of reporting errors directly in the read handler, the helper now returns errors via Error **. Sequence gaps are reported as warnings. Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-23-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-accel-stubs.c | 7 ++++ hw/arm/smmuv3-accel.c | 73 ++++++++++++++++++++++--------------- hw/arm/smmuv3-accel.h | 2 + 3 files changed, 52 insertions(+), 30 deletions(-) diff --git a/hw/arm/smmuv3-accel-stubs.c b/hw/arm/smmuv3-accel-stubs.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel-stubs.c +++ b/hw/arm/smmuv3-accel-stubs.c @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_alloc_veventq(SMMUv3State *s, Error **errp) return true; } +bool smmuv3_accel_event_read_validate(IOMMUFDVeventq *veventq, uint32_t type, + void *buf, size_t size, Error **errp) +{ + return true; +} + + void smmuv3_accel_reset(SMMUv3State *s) { } diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_issue_inv_cmd(SMMUv3State *bs, void *cmd, SMMUDevice *sdev, sizeof(Cmd), &entry_num, cmd, errp); } +bool smmuv3_accel_event_read_validate(IOMMUFDVeventq *veventq, uint32_t type, + void *buf, size_t size, Error **errp) +{ + uint32_t last_seq = veventq->last_event_seq; + uint32_t id = veventq->veventq_id; + struct iommufd_vevent_header *hdr; + ssize_t bytes; + + bytes = read(veventq->veventq_fd, buf, size); + if (bytes <= 0) { + if (errno == EAGAIN || errno == EINTR) { + return true; + } + error_setg(errp, "vEVENTQ(type %u id %u): read failed (%m)", type, id); + return false; + } + hdr = (struct iommufd_vevent_header *)buf; + if (bytes == sizeof(*hdr) && + (hdr->flags & IOMMU_VEVENTQ_FLAG_LOST_EVENTS)) { + error_setg(errp, "vEVENTQ(type %u id %u): overflowed", type, id); + veventq->event_start = false; + return false; + } + if (bytes < size) { + error_setg(errp, "vEVENTQ(type %u id %u): short read(%zd/%zd bytes)", + type, id, bytes, size); + return false; + } + /* Check sequence in hdr for lost events if any */ + if (veventq->event_start && (hdr->sequence - last_seq != 1)) { + warn_report("vEVENTQ(type %u id %u): lost %u event(s)", + type, id, hdr->sequence - last_seq - 1); + } + veventq->last_event_seq = hdr->sequence; + veventq->event_start = true; + return true; +} + static void smmuv3_accel_event_read(void *opaque) { SMMUv3State *s = opaque; @@ -XXX,XX +XXX,XX @@ static void smmuv3_accel_event_read(void *opaque) struct iommufd_vevent_header hdr; struct iommu_vevent_arm_smmuv3 vevent; } buf; - enum iommu_veventq_type type = IOMMU_VEVENTQ_TYPE_ARM_SMMUV3; - uint32_t id = veventq->veventq_id; - uint32_t last_seq = veventq->last_event_seq; - ssize_t bytes; + Error *local_err = NULL; - bytes = read(veventq->veventq_fd, &buf, sizeof(buf)); - if (bytes <= 0) { - if (errno == EAGAIN || errno == EINTR) { - return; - } - error_report_once("vEVENTQ(type %u id %u): read failed (%m)", type, id); + if (!smmuv3_accel_event_read_validate(veventq, + IOMMU_VEVENTQ_TYPE_ARM_SMMUV3, &buf, + sizeof(buf), &local_err)) { + warn_report_err_once(local_err); return; } - - if (bytes == sizeof(buf.hdr) && - (buf.hdr.flags & IOMMU_VEVENTQ_FLAG_LOST_EVENTS)) { - error_report_once("vEVENTQ(type %u id %u): overflowed", type, id); - veventq->event_start = false; - return; - } - if (bytes < sizeof(buf)) { - error_report_once("vEVENTQ(type %u id %u): short read(%zd/%zd bytes)", - type, id, bytes, sizeof(buf)); - return; - } - - /* Check sequence in hdr for lost events if any */ - if (veventq->event_start && (buf.hdr.sequence - last_seq != 1)) { - error_report_once("vEVENTQ(type %u id %u): lost %u event(s)", - type, id, buf.hdr.sequence - last_seq - 1); - } - veventq->last_event_seq = buf.hdr.sequence; - veventq->event_start = true; smmuv3_propagate_event(s, (Evt *)&buf.vevent); } diff --git a/hw/arm/smmuv3-accel.h b/hw/arm/smmuv3-accel.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.h +++ b/hw/arm/smmuv3-accel.h @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_issue_inv_cmd(SMMUv3State *s, void *cmd, SMMUDevice *sdev, Error **errp); void smmuv3_accel_idr_override(SMMUv3State *s); bool smmuv3_accel_alloc_veventq(SMMUv3State *s, Error **errp); +bool smmuv3_accel_event_read_validate(IOMMUFDVeventq *veventq, uint32_t type, + void *buf, size_t size, Error **errp); void smmuv3_accel_reset(SMMUv3State *s); #endif /* HW_ARM_SMMUV3_ACCEL_H */ -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> Install an event handler on the CMDQV vEVENTQ fd to read and propagate host received CMDQV errors to the guest. The handler runs in QEMU's main loop, using a non-blocking fd registered via qemu_set_fd_handler(). Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-24-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/tegra241-cmdqv.c | 63 +++++++++++++++++++++++++++++++++++++++++ hw/arm/trace-events | 1 + 2 files changed, 64 insertions(+) diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.c +++ b/hw/arm/tegra241-cmdqv.c @@ -XXX,XX +XXX,XX @@ #include "hw/arm/smmuv3.h" #include "hw/arm/smmuv3-common.h" +#include "hw/core/irq.h" #include "smmuv3-accel.h" #include "tegra241-cmdqv.h" #include "trace.h" @@ -XXX,XX +XXX,XX @@ out: trace_tegra241_cmdqv_write_mmio(offset, value, size); } +static void tegra241_cmdqv_event_read(void *opaque) +{ + Tegra241CMDQV *cmdqv = opaque; + IOMMUFDVeventq *veventq = cmdqv->veventq; + struct { + struct iommufd_vevent_header hdr; + struct iommu_vevent_tegra241_cmdqv vevent; + } buf; + Error *local_err = NULL; + + if (!smmuv3_accel_event_read_validate(veventq, + IOMMU_VEVENTQ_TYPE_TEGRA241_CMDQV, + &buf, sizeof(buf), &local_err)) { + warn_report_err_once(local_err); + return; + } + + if (buf.vevent.lvcmdq_err_map[0] || buf.vevent.lvcmdq_err_map[1]) { + cmdqv->vintf_cmdq_err_map[0] = + extract64(buf.vevent.lvcmdq_err_map[0], 0, 32); + cmdqv->vintf_cmdq_err_map[1] = + extract64(buf.vevent.lvcmdq_err_map[0], 32, 32); + cmdqv->vintf_cmdq_err_map[2] = + extract64(buf.vevent.lvcmdq_err_map[1], 0, 32); + cmdqv->vintf_cmdq_err_map[3] = + extract64(buf.vevent.lvcmdq_err_map[1], 32, 32); + /* + * CMDQV_CMDQ_ERR_MAP and VINTF0_LVCMDQ_ERR_MAP are distinct + * registers (different MMIO offsets). With only VINTF0 exposed + * they carry the same data, so mirror. + */ + for (int i = 0; i < 4; i++) { + cmdqv->cmdq_err_map[i] = cmdqv->vintf_cmdq_err_map[i]; + } + /* Set the VINTF0 bit in VI_ERR_MAP_0 (only VINTF0 is exposed). */ + cmdqv->vi_err_map[0] |= BIT(0); + if (!(cmdqv->vi_int_mask[0] & BIT(0))) { + qemu_irq_pulse(cmdqv->irq); + } + trace_tegra241_cmdqv_err_map( + cmdqv->vintf_cmdq_err_map[3], cmdqv->vintf_cmdq_err_map[2], + cmdqv->vintf_cmdq_err_map[1], cmdqv->vintf_cmdq_err_map[0]); + } +} + static void tegra241_cmdqv_free_viommu(SMMUv3State *s) { SMMUv3AccelState *accel = s->s_accel; @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_free_viommu(SMMUv3State *s) return; } if (veventq) { + qemu_set_fd_handler(veventq->veventq_fd, NULL, NULL, NULL); close(veventq->veventq_fd); iommufd_backend_free_id(viommu->iommufd, veventq->veventq_id); g_free(veventq); @@ -XXX,XX +XXX,XX @@ tegra241_cmdqv_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, Tegra241CMDQV *cmdqv = s->s_accel->cmdqv; uint32_t viommu_id, veventq_id, veventq_fd; IOMMUFDVeventq *veventq; + int flags; if (!iommufd_backend_alloc_viommu(idev->iommufd, idev->devid, IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV, @@ -XXX,XX +XXX,XX @@ tegra241_cmdqv_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, goto munmap_page0; } + flags = fcntl(veventq_fd, F_GETFL); + if (flags < 0) { + error_setg(errp, "Failed to get flags for vEVENTQ fd"); + goto free_veventq; + } + if (fcntl(veventq_fd, F_SETFL, O_NONBLOCK | flags) < 0) { + error_setg(errp, "Failed to set O_NONBLOCK on vEVENTQ fd"); + goto free_veventq; + } + veventq = g_new(IOMMUFDVeventq, 1); veventq->veventq_id = veventq_id; veventq->veventq_fd = veventq_fd; cmdqv->veventq = veventq; + /* Set up event handler for veventq fd */ + qemu_set_fd_handler(veventq_fd, tegra241_cmdqv_event_read, NULL, cmdqv); *out_viommu_id = viommu_id; return true; +free_veventq: + close(veventq_fd); + iommufd_backend_free_id(idev->iommufd, veventq_id); munmap_page0: munmap(cmdqv->vintf_page0, VINTF_PAGE_SIZE); cmdqv->vintf_page0 = NULL; diff --git a/hw/arm/trace-events b/hw/arm/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -XXX,XX +XXX,XX @@ smmuv3_accel_install_ste(uint32_t vsid, const char * type, uint32_t hwpt_id) "vS # tegra241-cmdqv tegra241_cmdqv_read_mmio(uint64_t offset, uint64_t val, unsigned size) "offset: 0x%"PRIx64" val: 0x%"PRIx64" size: 0x%x" tegra241_cmdqv_write_mmio(uint64_t offset, uint64_t val, unsigned size) "offset: 0x%"PRIx64" val: 0x%"PRIx64" size: 0x%x" +tegra241_cmdqv_err_map(uint32_t map3, uint32_t map2, uint32_t map1, uint32_t map0) "hw irq received. error (hex) maps: %04X:%04X:%04X:%04X" tegra241_cmdqv_read_vcmdq_page0(int index, const char *aperture, const char *backing, uint64_t offset0, uint64_t val) "vcmdq[%d] %s (%s) offset0: 0x%"PRIx64" val: 0x%"PRIx64 tegra241_cmdqv_read_vcmdq_page1(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64 tegra241_cmdqv_write_vcmdq_page0(int index, const char *aperture, const char *backing, uint64_t offset0, uint64_t val) "vcmdq[%d] %s (%s) offset0: 0x%"PRIx64" val: 0x%"PRIx64 -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> Initialize the Tegra241 CMDQV register state in the reset handler. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-25-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/tegra241-cmdqv.c | 38 ++++++++++++++++++++++++++++++++++++++ hw/arm/tegra241-cmdqv.h | 3 +++ hw/arm/trace-events | 1 + 3 files changed, 42 insertions(+) diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.c +++ b/hw/arm/tegra241-cmdqv.c @@ -XXX,XX +XXX,XX @@ free_viommu: return false; } +static void tegra241_cmdqv_init_regs(SMMUv3State *s, Tegra241CMDQV *cmdqv) +{ + int i; + + cmdqv->config = V_CONFIG_RESET; + cmdqv->param = FIELD_DP32(0, PARAM, CMDQV_VER, CMDQV_VER); + cmdqv->param = FIELD_DP32(cmdqv->param, PARAM, CMDQV_NUM_CMDQ_LOG2, + CMDQV_NUM_CMDQ_LOG2); + cmdqv->param = FIELD_DP32(cmdqv->param, PARAM, CMDQV_NUM_SID_PER_VI_LOG2, + CMDQV_NUM_SID_PER_VI_LOG2); + trace_tegra241_cmdqv_init_regs(cmdqv->param); + cmdqv->status = R_STATUS_CMDQV_ENABLED_MASK; + + for (i = 0; i < 2; i++) { + cmdqv->vi_err_map[i] = 0; + cmdqv->vi_int_mask[i] = 0; + } + for (i = 0; i < 4; i++) { + cmdqv->cmdq_err_map[i] = 0; + cmdqv->vintf_cmdq_err_map[i] = 0; + } + cmdqv->vintf_config = 0; + cmdqv->vintf_status = 0; + for (i = 0; i < TEGRA241_CMDQV_MAX_CMDQ; i++) { + cmdqv->cmdq_alloc_map[i] = 0; + cmdqv->vcmdq_cons_indx[i] = 0; + cmdqv->vcmdq_prod_indx[i] = 0; + cmdqv->vcmdq_config[i] = 0; + cmdqv->vcmdq_status[i] = 0; + cmdqv->vcmdq_gerror[i] = 0; + cmdqv->vcmdq_gerrorn[i] = 0; + cmdqv->vcmdq_base[i] = 0; + cmdqv->vcmdq_cons_indx_base[i] = 0; + } +} + static void tegra241_cmdqv_reset(SMMUv3State *s) { Tegra241CMDQV *cmdqv = s->s_accel->cmdqv; @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_reset(SMMUv3State *s) tegra241_cmdqv_guest_unmap_vintf_page0(cmdqv); tegra241_cmdqv_free_all_vcmdq(cmdqv); + + tegra241_cmdqv_init_regs(s, cmdqv); } static const MemoryRegionOps mmio_cmdqv_ops = { diff --git a/hw/arm/tegra241-cmdqv.h b/hw/arm/tegra241-cmdqv.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.h +++ b/hw/arm/tegra241-cmdqv.h @@ -XXX,XX +XXX,XX @@ FIELD(CONFIG, CMDQ_MAX_CLK_BATCH, 4, 8) FIELD(CONFIG, CMDQ_MAX_CMD_BATCH, 12, 8) FIELD(CONFIG, CONS_DRAM_EN, 20, 1) +/* CMDQV_EN=1, PER_CMD_OFFSET=16B, CLK_BATCH=256, CMD_BATCH=32. */ +#define V_CONFIG_RESET 0x00020083 + REG32(PARAM, 0x4) FIELD(PARAM, CMDQV_VER, 0, 4) FIELD(PARAM, CMDQV_NUM_CMDQ_LOG2, 4, 4) diff --git a/hw/arm/trace-events b/hw/arm/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -XXX,XX +XXX,XX @@ smmuv3_accel_install_ste(uint32_t vsid, const char * type, uint32_t hwpt_id) "vS tegra241_cmdqv_read_mmio(uint64_t offset, uint64_t val, unsigned size) "offset: 0x%"PRIx64" val: 0x%"PRIx64" size: 0x%x" tegra241_cmdqv_write_mmio(uint64_t offset, uint64_t val, unsigned size) "offset: 0x%"PRIx64" val: 0x%"PRIx64" size: 0x%x" tegra241_cmdqv_err_map(uint32_t map3, uint32_t map2, uint32_t map1, uint32_t map0) "hw irq received. error (hex) maps: %04X:%04X:%04X:%04X" +tegra241_cmdqv_init_regs(uint32_t param) "register init, param=0x%08X" tegra241_cmdqv_read_vcmdq_page0(int index, const char *aperture, const char *backing, uint64_t offset0, uint64_t val) "vcmdq[%d] %s (%s) offset0: 0x%"PRIx64" val: 0x%"PRIx64 tegra241_cmdqv_read_vcmdq_page1(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64 tegra241_cmdqv_write_vcmdq_page0(int index, const char *aperture, const char *backing, uint64_t offset0, uint64_t val) "vcmdq[%d] %s (%s) offset0: 0x%"PRIx64" val: 0x%"PRIx64 -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> CMDQV HW performs DMA accesses to guest queue memory by its host physical address set up via IOMMUFD. This requires the guest queue to be contiguous in both guest PA and host PA space. With Tegra241 CMDQV enabled, we must only advertise a command queue size (CMDQS) that the host can safely back with physically contiguous memory. Allowing a queue size larger than the host page size could cause the hardware to DMA across page boundaries, leading to faults. Use qemu_minrampagesize() to find the smallest memory-backend page size in use, then cap IDR1.CMDQS so the guest cannot configure a command queue that exceeds that contiguous backing. Note this is done at SMMUv3 init, before any guest queue GPA is known, so the cap is conservative. Maximum queue size is 8MiB; it is recommended to back the VM with hugepage sizes large enough so CMDQS stays at the HW maximum. Smaller backing pages reduce CMDQS accordingly. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-26-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/tegra241-cmdqv.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.c +++ b/hw/arm/tegra241-cmdqv.c @@ -XXX,XX +XXX,XX @@ #include "hw/arm/smmuv3-common.h" #include "hw/core/irq.h" #include "smmuv3-accel.h" +#include "smmuv3-internal.h" +#include "system/hostmem.h" #include "tegra241-cmdqv.h" #include "trace.h" @@ -XXX,XX +XXX,XX @@ free_viommu: static void tegra241_cmdqv_init_regs(SMMUv3State *s, Tegra241CMDQV *cmdqv) { int i; + long pgsize; + uint32_t val; cmdqv->config = V_CONFIG_RESET; cmdqv->param = FIELD_DP32(0, PARAM, CMDQV_VER, CMDQV_VER); @@ -XXX,XX +XXX,XX @@ static void tegra241_cmdqv_init_regs(SMMUv3State *s, Tegra241CMDQV *cmdqv) cmdqv->vcmdq_base[i] = 0; cmdqv->vcmdq_cons_indx_base[i] = 0; } + + /* + * CMDQ must not cross a physical RAM backend page. Adjust CMDQS so the + * queue fits entirely within the smallest backend page size, ensuring + * the command queue is physically contiguous in host memory. + * + * IDR1.CMDQS = log2(max_qsz) - entry_shift + * + * where entry_shift = 4 (each CMDQ entry is 16 bytes = 2^4). + */ + pgsize = qemu_minrampagesize(); + if (pgsize == LONG_MAX) { + pgsize = qemu_real_host_page_size(); + } + val = FIELD_EX32(s->idr[1], IDR1, CMDQS); + s->idr[1] = FIELD_DP32(s->idr[1], IDR1, CMDQS, MIN(ctz64(pgsize) - 4, val)); } static void tegra241_cmdqv_reset(SMMUv3State *s) -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> Add an "identifier" property to the SMMUv3 device and use it when building the ACPI IORT SMMUv3 node Identifier field. This avoids relying on device enumeration order and provides a stable per-device identifier. A subsequent patch will use the same identifier when generating the DSDT description for Tegra241 CMDQV, ensuring that the IORT and DSDT entries refer to the same SMMUv3 instance. The identifier is assigned at pre-plug time, accounting for the ITS Group node that build_iort() places before SMMUv3 nodes in the IORT table, so that identifiers are globally unique across all IORT nodes. No functional change: IORT blob content for bios-tables qtest is identical to before. Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-27-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3.c | 2 ++ hw/arm/virt-acpi-build.c | 5 ++++- hw/arm/virt.c | 12 ++++++++++++ include/hw/arm/smmuv3.h | 1 + 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -XXX,XX +XXX,XX @@ static const Property smmuv3_properties[] = { * Defaults to stage 1 */ DEFINE_PROP_STRING("stage", SMMUv3State, stage), + /* Identifier used for ACPI IORT SMMUv3 (and DSDT for CMDQV) generation */ + DEFINE_PROP_UINT8("identifier", SMMUv3State, identifier, 0), DEFINE_PROP_BOOL("accel", SMMUv3State, accel, false), /* GPA of MSI doorbell, for SMMUv3 accel use. */ DEFINE_PROP_UINT64("msi-gpa", SMMUv3State, msi_gpa, 0), diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -XXX,XX +XXX,XX @@ static int iort_idmap_compare(gconstpointer a, gconstpointer b) typedef struct AcpiIortSMMUv3Dev { int irq; hwaddr base; + uint8_t id; GArray *rc_smmu_idmaps; /* Offset of the SMMUv3 IORT Node relative to the start of the IORT */ size_t offset; @@ -XXX,XX +XXX,XX @@ static int populate_smmuv3_dev(VirtMachineState *vms, GArray *sdev_blob) &error_abort)); sdev.accel = object_property_get_bool(obj, "accel", &error_abort); sdev.ats = smmuv3_ats_enabled(ARM_SMMUV3(obj)); + sdev.id = object_property_get_uint(obj, "identifier", &error_abort); pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); sbdev = SYS_BUS_DEVICE(obj); sdev.base = platform_bus_get_mmio_addr(pbus, sbdev, 0); @@ -XXX,XX +XXX,XX @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) (ID_MAPPING_ENTRY_SIZE * smmu_mapping_count); build_append_int_noprefix(table_data, node_size, 2); /* Length */ build_append_int_noprefix(table_data, 4, 1); /* Revision */ - build_append_int_noprefix(table_data, id++, 4); /* Identifier */ + build_append_int_noprefix(table_data, sdev->id, 4); /* Identifier */ + id++; /* advance shared counter for RC/RMR node uniqueness */ /* Number of ID mappings */ build_append_int_noprefix(table_data, smmu_mapping_count, 4); /* Reference to ID Array */ diff --git a/hw/arm/virt.c b/hw/arm/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -XXX,XX +XXX,XX @@ static MemMapEntry extended_memmap[] = { /* Any CXL Fixed memory windows come here */ }; +/* Counts SMMUv3 devices plugged; used to assign stable IORT identifiers */ +static uint8_t smmuv3_dev_id; + static const int a15irqmap[] = { [VIRT_UART0] = 1, [VIRT_RTC] = 2, @@ -XXX,XX +XXX,XX @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, OBJECT(vms->sysmem), NULL); object_property_set_link(OBJECT(dev), "secure-memory", OBJECT(vms->secure_sysmem), NULL); + /* + * In build_iort(), the ITS node(id=0) precedes SMMUv3 nodes + * when present. Account for it so this SMMUv3's identifier + * is globally unique across all IORT nodes. + */ + uint8_t its_offset = (vms->msi_controller == VIRT_MSI_CTRL_ITS) + ? 1 : 0; + object_property_set_uint(OBJECT(dev), "identifier", + its_offset + smmuv3_dev_id++, NULL); } if (object_property_get_bool(OBJECT(dev), "accel", &error_abort)) { hwaddr db_start = 0; diff --git a/include/hw/arm/smmuv3.h b/include/hw/arm/smmuv3.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/smmuv3.h +++ b/include/hw/arm/smmuv3.h @@ -XXX,XX +XXX,XX @@ struct SMMUv3State { qemu_irq irq[4]; QemuMutex mutex; char *stage; + uint8_t identifier; /* SMMU has HW accelerator support for nested S1 + s2 */ bool accel; -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> Introduce a SMMUv3AccelCmdqvType enum and a helper to query the CMDQV implementation type associated with an accelerated SMMUv3 instance. A subsequent patch will use this helper when generating the Tegra241 CMDQV DSDT. Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-28-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-accel-stubs.c | 5 +++++ hw/arm/smmuv3-accel.c | 12 ++++++++++++ hw/arm/smmuv3-accel.h | 10 ++++++++++ hw/arm/tegra241-cmdqv.c | 6 ++++++ 4 files changed, 33 insertions(+) diff --git a/hw/arm/smmuv3-accel-stubs.c b/hw/arm/smmuv3-accel-stubs.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel-stubs.c +++ b/hw/arm/smmuv3-accel-stubs.c @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_event_read_validate(IOMMUFDVeventq *veventq, uint32_t type, void smmuv3_accel_reset(SMMUv3State *s) { } + +SMMUv3AccelCmdqvType smmuv3_accel_cmdqv_type(Object *obj) +{ + return SMMUV3_CMDQV_NONE; +} diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -XXX,XX +XXX,XX @@ static void smmuv3_accel_as_init(SMMUv3State *s) address_space_init(shared_as_sysmem, &root, "smmuv3-accel-as-sysmem"); } +SMMUv3AccelCmdqvType smmuv3_accel_cmdqv_type(Object *obj) +{ + SMMUv3State *s = ARM_SMMUV3(obj); + SMMUv3AccelState *accel = s->s_accel; + + if (!accel || !accel->cmdqv_ops || !accel->cmdqv_ops->get_type) { + return SMMUV3_CMDQV_NONE; + } + + return accel->cmdqv_ops->get_type(); +} + static void smmuv3_accel_machine_done(Notifier *notifier, void *data) { SMMUv3State *s = container_of(notifier, SMMUv3State, machine_done); diff --git a/hw/arm/smmuv3-accel.h b/hw/arm/smmuv3-accel.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.h +++ b/hw/arm/smmuv3-accel.h @@ -XXX,XX +XXX,XX @@ #include <linux/iommufd.h> #endif +typedef enum SMMUv3AccelCmdqvType { + SMMUV3_CMDQV_NONE = 0, + SMMUV3_CMDQV_TEGRA241, +} SMMUv3AccelCmdqvType; + /* * CMDQ-Virtualization (CMDQV) hardware support, extends the SMMUv3 to * support multiple VCMDQs with virtualization capabilities. @@ -XXX,XX +XXX,XX @@ typedef struct SMMUv3AccelCmdqvOps { * If NULL, the viommu_id is freed directly via iommufd_backend_free_id(). */ void (*free_viommu)(SMMUv3State *s); + /** + * @get_type: Optional callback. Return the CMDQV implementation type. + */ + SMMUv3AccelCmdqvType (*get_type)(void); /** * @reset: Optional callback. Reset CMDQV state. */ @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_alloc_veventq(SMMUv3State *s, Error **errp); bool smmuv3_accel_event_read_validate(IOMMUFDVeventq *veventq, uint32_t type, void *buf, size_t size, Error **errp); void smmuv3_accel_reset(SMMUv3State *s); +SMMUv3AccelCmdqvType smmuv3_accel_cmdqv_type(Object *obj); #endif /* HW_ARM_SMMUV3_ACCEL_H */ diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.c +++ b/hw/arm/tegra241-cmdqv.c @@ -XXX,XX +XXX,XX @@ static bool tegra241_cmdqv_init(SMMUv3State *s, Error **errp) return true; } +static SMMUv3AccelCmdqvType tegra241_cmdqv_get_type(void) +{ + return SMMUV3_CMDQV_TEGRA241; +} + static bool tegra241_cmdqv_probe(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, Error **errp) { @@ -XXX,XX +XXX,XX @@ static const SMMUv3AccelCmdqvOps tegra241_cmdqv_ops = { .init = tegra241_cmdqv_init, .alloc_viommu = tegra241_cmdqv_alloc_viommu, .free_viommu = tegra241_cmdqv_free_viommu, + .get_type = tegra241_cmdqv_get_type, .reset = tegra241_cmdqv_reset, }; -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> Add ACPI DSDT support for Tegra241 CMDQV when the SMMUv3 instance is created with tegra241-cmdqv. For each accelerated SMMUv3 instance, add a Tegra241 CMDQV device object under the DSDT \_SB namespace, with HID "NVDA200C" and a UID that matches the Identifier of the corresponding SMMUv3 IORT node, so the guest OS can associate the DSDT device with the right SMMU. The _CRS covers the CMDQV MMIO aperture plus its interrupt, and _CCA declares I/O cache coherency. See ACPI Specification 6.5, Section 6 (Device Configuration) for _HID/_UID/_CCA/_CRS. Generated DSDT entry for a CMDQV instance paired with SMMUv3 Identifier=1: ... Device (CV01) { Name (_HID, "NVDA200C") // _HID: Hardware ID Name (_UID, One) // _UID: Unique ID Name (_CCA, One) // _CCA: Cache Coherency Attribute Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, 0x0000000000000000, // Granularity 0x000000000C080000, // Range Minimum 0x000000000C0CFFFF, // Range Maximum 0x0000000000000000, // Translation Offset 0x0000000000050000, // Length ,, , AddressRangeMemory, TypeStatic) Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, ) { 0x00000094, } }) } ... Generated IORT SMMUv3 node (Identifier = 1): ... [048h 0072 001h] Type : 04 [049h 0073 002h] Length : 0058 [04Bh 0075 001h] Revision : 04 [04Ch 0076 004h] Identifier : 00000001 [050h 0080 004h] Mapping Count : 00000001 [054h 0084 004h] Mapping Offset : 00000044 ... Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Message-id: 20260609112552.378999-29-skolothumtho@nvidia.com Co-developed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/trace-events | 1 + hw/arm/virt-acpi-build.c | 52 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/hw/arm/trace-events b/hw/arm/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -XXX,XX +XXX,XX @@ omap1_lpg_led(const char *onoff) "omap1 LPG: LED is %s" # virt-acpi-build.c virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out." +virt_acpi_dsdt_tegra241_cmdqv(int smmu_id, uint64_t base, uint32_t irq) "DSDT: add cmdqv node for (id=%d), base=0x%" PRIx64 ", irq=%d" # smmu-common.c smmu_add_mr(const char *name) "%s" diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -XXX,XX +XXX,XX @@ #include "target/arm/cpu.h" #include "target/arm/multiprocessing.h" +#include "smmuv3-accel.h" +#include "tegra241-cmdqv.h" + #define ARM_SPI_BASE 32 #define ACPI_BUILD_TABLE_SIZE 0x20000 @@ -XXX,XX +XXX,XX @@ static void build_fadt_rev6(GArray *table_data, BIOSLinker *linker, build_fadt(table_data, linker, &fadt, vms->oem_id, vms->oem_table_id); } +static void acpi_dsdt_add_tegra241_cmdqv(Aml *scope, VirtMachineState *vms) +{ + for (int i = 0; i < vms->smmuv3_devices->len; i++) { + Object *obj = OBJECT(g_ptr_array_index(vms->smmuv3_devices, i)); + PlatformBusDevice *pbus; + Aml *dev, *crs, *addr; + SysBusDevice *sbdev; + hwaddr base; + uint32_t id; + int irq; + + if (smmuv3_accel_cmdqv_type(obj) != SMMUV3_CMDQV_TEGRA241) { + continue; + } + id = object_property_get_uint(obj, "identifier", &error_abort); + pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); + sbdev = SYS_BUS_DEVICE(obj); + base = platform_bus_get_mmio_addr(pbus, sbdev, 1); + base += vms->memmap[VIRT_PLATFORM_BUS].base; + irq = platform_bus_get_irqn(pbus, sbdev, NUM_SMMU_IRQS); + irq += vms->irqmap[VIRT_PLATFORM_BUS]; + irq += ARM_SPI_BASE; + + dev = aml_device("CV%.02u", id); + aml_append(dev, aml_name_decl("_HID", aml_string("NVDA200C"))); + aml_append(dev, aml_name_decl("_UID", aml_int(id))); + aml_append(dev, aml_name_decl("_CCA", aml_int(1))); + + crs = aml_resource_template(); + addr = aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, + AML_CACHEABLE, AML_READ_WRITE, 0x0, base, + base + TEGRA241_CMDQV_IO_LEN - 0x1, 0x0, + TEGRA241_CMDQV_IO_LEN); + aml_append(crs, addr); + aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE, + AML_ACTIVE_HIGH, AML_EXCLUSIVE, + (uint32_t *)&irq, 1)); + aml_append(dev, aml_name_decl("_CRS", crs)); + + aml_append(scope, dev); + + trace_virt_acpi_dsdt_tegra241_cmdqv(id, base, irq); + } +} + /* DSDT */ static void build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) @@ -XXX,XX +XXX,XX @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) acpi_dsdt_add_tpm(scope, vms); #endif + if (!vms->legacy_smmuv3_present) { + acpi_dsdt_add_tegra241_cmdqv(scope, vms); + } + aml_append(dsdt, scope); pci0_scope = aml_scope("\\_SB.PCI0"); -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> When CMDQV is active, the first cold-plugged VFIO device establishes the viommu to host SMMUv3 association, and the guest's boot-time CMDQV configuration (VINTFs, VCMDQs) is built on top of that association. Hot-unplugging that device would release the viommu and tear down all CMDQV state. Hot-plugging another device behind a different host SMMUv3+CMDQV would then re-bind the same vSMMUv3 to new host hardware, while the guest keeps using its boot-time configuration and ends up issuing commands to the wrong host. Block hot-unplug of the establishing device to avoid this; retaining the binding across unplug is non-trivial and not required by any current use case. Also abort at machine_done if cmdqv=on is requested but no cold-plugged VFIO device was present to initialize it. Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-30-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-accel.c | 18 ++++++++++++++++++ hw/arm/smmuv3-accel.h | 1 + 2 files changed, 19 insertions(+) diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -XXX,XX +XXX,XX @@ static bool smmuv3_accel_set_iommu_device(PCIBus *bus, void *opaque, int devfn, return false; } + /* + * CMDQV is active: block hot-unplug of the device that established the + * viommu association. Removing it would cause the vIOMMU to host SMMUv3 + * association be changed via device hot-plug. + */ + if (s->s_accel->cmdqv_ops) { + PCIDevice *pdev = pci_find_device(bus, pci_bus_num(bus), devfn); + error_setg(&accel_dev->unplug_blocker, + "CMDQV is active: removing the device that established the " + "viommu association would break the guest CMDQV"); + qdev_add_unplug_blocker(DEVICE(pdev), accel_dev->unplug_blocker); + } done: accel_dev->hiodi = hiodi; accel_dev->s_accel = s->s_accel; @@ -XXX,XX +XXX,XX @@ static void smmuv3_accel_machine_done(Notifier *notifier, void *data) "at least one cold-plugged VFIO device"); exit(1); } + + if (s->cmdqv == ON_OFF_AUTO_ON && !accel->cmdqv) { + error_report("arm-smmuv3 cmdqv=on requires at least one cold-plugged " + "VFIO device"); + exit(1); + } } bool smmuv3_accel_init(SMMUv3State *s, Error **errp) diff --git a/hw/arm/smmuv3-accel.h b/hw/arm/smmuv3-accel.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.h +++ b/hw/arm/smmuv3-accel.h @@ -XXX,XX +XXX,XX @@ typedef struct SMMUv3AccelDevice { IOMMUFDVdev *vdev; QLIST_ENTRY(SMMUv3AccelDevice) next; SMMUv3AccelState *s_accel; + Error *unplug_blocker; /* set when CMDQV is active to block hot-unplug */ } SMMUv3AccelDevice; bool smmuv3_accel_init(SMMUv3State *s, Error **errp); -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> Add an overview describing the Tegra241 CMDQV passthrough model, MMIO layout, guest-driven lifecycle, and per-VM isolation. Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Message-id: 20260609112552.378999-31-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/tegra241-cmdqv.c | 100 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/tegra241-cmdqv.c +++ b/hw/arm/tegra241-cmdqv.c @@ -XXX,XX +XXX,XX @@ * SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Tegra241 CMDQV - overview + * ========================= + * + * NVIDIA Tegra241 extends SMMUv3 with a Command Queue Virtualization (CMDQ-V) + * block. It lets a guest issue SMMU invalidation commands directly to + * dedicated hardware queues (vCMDQs) without trapping into the hypervisor on + * the fast path. vCMDQs are exclusively allocated to Virtual Interfaces + * (VINTFs); the host kernel allocates one VINTF per emulated SMMUv3 instance + * via iommufd. QEMU emulates the CMDQV MMIO region and drives the host kernel + * calls (VIOMMU_ALLOC, HW_QUEUE_ALLOC, mmap); the actual command processing + * happens on real hardware. + * + * A vCMDQ becomes functional only once allocated to the host VINTF; until then + * no command processing happens, and trapped register accesses fall back to a + * QEMU-side cache. After allocation, the cached register state is migrated to + * the hardware and command processing runs on the host; guest accesses to the + * live control/status registers then bypass QEMU and reach the host directly. + * + * MMIO layout (64KB pages, total TEGRA241_CMDQV_IO_LEN) + * ----------------------------------------------------- + * 0x00000 CMDQV Config page: QEMU-trapped. + * 0x10000 Direct vCMDQ Page 0 (control/status): QEMU-trapped and routed + * to either the mmap'd host VINTF Page 0 (if the vCMDQ has been + * allocated to a VINTF) or a per-vCMDQ register cache (otherwise). + * 0x20000 Direct vCMDQ Page 1 (BASE / DRAM addresses): QEMU-trapped. + * 0x30000 VINTF Page 0 (per-VINTF control/status): the guest's virtual + * VINTF Page 0 aperture, backed by the host VINTF Page 0 (mmap'd + * via iommufd) and installed into guest MMIO as a RAM-device + * subregion when VINTF is enabled; subsequent accesses bypass QEMU. + * 0x40000 VINTF Page 1 (per-VINTF BASE): QEMU-trapped. Although this is + * a HW alias of the direct Page 1, the kernel only exposes mmap + * for the host VINTF Page 0; the host VINTF Page 1 is not mmap'd + * and stays trapped. + * + * The direct vCMDQ apertures (0x10000/0x20000) are HW aliases of the VINTF + * apertures (0x30000/0x40000); they expose the same per-vCMDQ register slots + * under different addressing. + * + * The direct vCMDQ Page 0 stays trapped rather than aliased to the host VINTF + * Page 0 mmap. The CMDQV architecture allows software to program a vCMDQ + * through the direct aperture before allocating it to a VINTF; aliasing to + * the host VINTF Page 0 mmap would route those accesses into unallocated + * logical slots where the hardware silently drops them, so trapping keeps + * accesses well-defined for an unallocated vCMDQ. + * + * Lifecycle (driven by guest events) + * ---------------------------------- + * 1. First vfio-pci device attach (.set_iommu_device) triggers: + * - tegra241_cmdqv_probe(): IOMMU_GET_HW_INFO confirms host CMDQV support. + * - IOMMU_VIOMMU_ALLOC: the kernel allocates and enables a VINTF for this + * VM, configures the VM's VMID (from its stage-2 HWPT) in VINTF_CONFIG, + * forces HYP_OWN=0, and returns the mmap offset/length for the host + * VINTF Page 0, which QEMU then mmap()s. + * + * 2. Guest writes VINTF_CONFIG.ENABLE = 1: + * QEMU installs the mmap'd host VINTF Page 0 into guest MMIO as the guest's + * virtual VINTF Page 0 aperture (a RAM-device subregion) and reports + * STATUS.ENABLE_OK = 1. The aperture is now a direct window onto the host + * page, so accesses no longer trap into QEMU; a vCMDQ within it operates as + * a real command queue only once it has been allocated (step 3). + * + * 3. Guest completes vCMDQ setup (BASE, CMDQ_ALLOC_MAP.ALLOC, CMDQV_EN, + * VINTF.ENABLE, in any order; each precondition write retries the HW queue + * allocation): + * IOMMU_HW_QUEUE_ALLOC grants the guest a new host vCMDQ in this VM's + * VINTF, binding the guest BASE GPA (translated through stage-2 and pinned + * by the kernel) to it. + * + * 4. Guest SMMU driver programs a Stream Table Entry for a passthrough + * device: IOMMU_VDEVICE_ALLOC programs SID_MATCH/SID_REPLACE in this VM's + * VINTF so that the HW translates the device's guest vSID into its host + * pSID. Commands referencing unmapped SIDs are rejected by HW. + * + * This reflects the current accel SMMUv3 design, which allocates the + * vDEVICE when the guest programs the STE. + * + * Per-VM isolation + * ---------------- + * - Each VM has its own iommufd FD; all iommufd objects (VINTF, vdevices, + * hw_queues, mmap regions) belong to that FD. Cross-FD lookups fail, so + * one VM cannot reach another VM's IDs. + * - IOMMU_VIOMMU_ALLOC configures the VM's VMID in VINTF_CONFIG; the CMDQV + * hardware substitutes / checks VMID on every command the guest issues. + * - The kernel allocates the VINTF with HYP_OWN = 0, which restricts the + * guest to a safe subset of commands. + * - IOMMU_VDEVICE_ALLOC populates SID_MATCH/SID_REPLACE so invalidations + * only reach the host StreamIDs assigned to this VM (see step 4). + * - IOMMU_HW_QUEUE_ALLOC binds each vCMDQ to a single VINTF, so a guest + * cannot reach a vCMDQ that belongs to another VM. + * + * Limits exposed to the guest + * --------------------------- + * One VINTF per emulated SMMUv3 and two vCMDQs per VINTF. The HW maximum + * vCMDQ size is 8MiB, but the size QEMU exposes to the guest may be smaller. + * The queue must be physically contiguous in host memory, so QEMU caps the + * exposed size to the host memory-backend page size. Use hugepage backing to + * reach the 8MiB maximum. + */ + #include "qemu/osdep.h" #include "qemu/log.h" -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> Introduce a "cmdqv" property to enable Tegra241 CMDQV support. This is only enabled for accelerated SMMUv3 devices. Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-32-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3.c | 8 ++++++++ qemu-options.hx | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -XXX,XX +XXX,XX @@ static bool smmu_validate_property(SMMUv3State *s, Error **errp) "bits if accel=on"); return false; } + if (s->cmdqv == ON_OFF_AUTO_ON) { + error_setg(errp, "cmdqv can only be enabled if accel=on"); + return false; + } return true; } @@ -XXX,XX +XXX,XX @@ static const Property smmuv3_properties[] = { DEFINE_PROP_OAS_MODE("oas", SMMUv3State, oas, OAS_MODE_AUTO), DEFINE_PROP_SSIDSIZE_MODE("ssidsize", SMMUv3State, ssidsize, SSID_SIZE_MODE_AUTO), + DEFINE_PROP_ON_OFF_AUTO("cmdqv", SMMUv3State, cmdqv, ON_OFF_AUTO_AUTO), }; static void smmuv3_instance_init(Object *obj) @@ -XXX,XX +XXX,XX @@ static void smmuv3_class_init(ObjectClass *klass, const void *data) "than 0 is required to enable PASID support." "Please ensure the value does not exceed the maximum " "SubstreamID size supported by the host platform."); + object_class_property_set_description(klass, "cmdqv", + "Enable/disable CMDQV support (for accel=on). " + "Valid values are on, off, and auto. Defaults to auto."); } static int smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu, diff --git a/qemu-options.hx b/qemu-options.hx index XXXXXXX..XXXXXXX 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -XXX,XX +XXX,XX @@ SRST - With accel=off, auto is resolved to 0. + ``cmdqv=on|off|auto`` (default: auto) + Enable hardware Command Queue Virtualization (CMDQV) for the + SMMUv3 command queue. Currently only the NVIDIA Tegra241 CMDQV + implementation is supported. + + - With accel=on, auto means the value is automatically derived from the host SMMU. + - With accel=off, auto is resolved to 'off'. + ``-device amd-iommu[,option=...]`` Enables emulation of an AMD-Vi I/O Memory Management Unit (IOMMU). Only available with ``-machine q35``, it supports the following options: -- 2.43.0
From: Kyle Fox <kylefoxaustin.github@gmail.com> M-profile CCR.BFHFNMIGN lets software executing at a negative execution priority (in HardFault/NMI, or with FAULTMASK set) suppress precise data BusFaults caused by load/store instructions: the access completes returning UNKNOWN data, the fault status is recorded in BFSR/BFAR, but no BusFault exception is taken. Software uses this to probe for the presence of a device. QEMU stored CCR.BFHFNMIGN but never consumed it: arm_cpu_do_transaction_ failed() always raised the external abort, which arm_v7m_cpu_do_interrupt() pended as a BusFault and then escalated to a HardFault it could not take at priority -1, aborting the VM with "Lockup: can't escalate 3 to HardFault". Honour the bit in arm_cpu_do_transaction_failed(): when the access is a data access from M-profile code at negative priority with BFHFNMIGN set, record PRECISERR/BFARVALID and BFAR and return without raising, so the faulting instruction completes instead of re-faulting forever. Instruction fetches are unaffected, since BFHFNMIGN applies only to data accesses. The SG instruction's stack-word load is also an AccType_NORMAL data access that must honour BFHFNMIGN, but QEMU performs it manually in v7m_read_sg_stack_word() (outside the TCG TLB, so it never reaches arm_cpu_do_transaction_failed()). Apply the same suppression there: on a BusFault, record the status and, when BFHFNMIGN is set at negative priority, return the UNKNOWN data instead of pending ARMV7M_EXCP_BUS. The remaining manual EXCP_BUS sites (vector-table loads, stacking, unstacking) are AccType_VECTABLE/STACK/UNSTACK and are not required to honour the bit, so they are left unchanged. This surfaced running the real NXP i.MX 95 System Manager firmware on the emulated Cortex-M33: its SystemMemoryProbe() (set BFHFNMIGN + FAULTMASK, do the access, test CFSR.BFARVALID) locked up the VM. With this change the SM's debug-monitor memory-probe commands run and recover correctly. Signed-off-by: Kyle Fox <kylefoxaustin.github@gmail.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> [PMM: minor tweak to v7m_read_sg_stack_word() code] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/m_helper.c | 16 ++++++++++++++-- target/arm/tcg/tlb_helper.c | 24 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -XXX,XX +XXX,XX @@ static bool v7m_read_sg_stack_word(ARMCPU *cpu, ARMMMUIdx mmu_idx, env->v7m.cfsr[M_REG_NS] |= (R_V7M_CFSR_PRECISERR_MASK | R_V7M_CFSR_BFARVALID_MASK); env->v7m.bfar = addr; - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_BUS, false); - return false; + /* + * The SG instruction's stack-word load is an AccType_NORMAL data + * access, so CCR.BFHFNMIGN applies: at negative execution priority + * with BFHFNMIGN set, the BusFault is suppressed -- the access + * completes returning UNKNOWN data (status recorded above), with no + * BusFault exception pended. + */ + if (!((env->v7m.ccr[M_REG_NS] & R_V7M_CCR_BFHFNMIGN_MASK) && + armv7m_nvic_neg_prio_requested(env->nvic, env->v7m.secure))) { + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_BUS, false); + return false; + } + /* BusFault suppressed; data value is UNKNOWN, we choose 0 */ + value = 0; } *spdata = value; diff --git a/target/arm/tcg/tlb_helper.c b/target/arm/tcg/tlb_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/tlb_helper.c +++ b/target/arm/tcg/tlb_helper.c @@ -XXX,XX +XXX,XX @@ #include "helper.h" #include "internals.h" #include "cpu-features.h" +#include "hw/intc/armv7m_nvic.h" /* * Returns true if the stage 1 translation regime is using LPAE format page @@ -XXX,XX +XXX,XX @@ void arm_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, MemTxResult response, uintptr_t retaddr) { ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; ARMMMUFaultInfo fi = {}; + /* + * For M-profile, CCR.BFHFNMIGN lets software executing at a negative + * priority (in HardFault/NMI, or with FAULTMASK set) suppress precise + * data BusFaults from load/store instructions: the access completes + * returning UNKNOWN data (the store is dropped), the fault status is + * recorded in BFSR/BFAR, but no BusFault exception is taken. This is + * the mechanism software uses to probe for the presence of a device + * (e.g. the NXP System Manager's SystemMemoryProbe). Honour it by + * recording the status and returning without raising, so the faulting + * instruction completes rather than re-faulting forever. BFHFNMIGN + * applies only to data accesses, so instruction fetches are unaffected. + */ + if (arm_feature(env, ARM_FEATURE_M) && + access_type != MMU_INST_FETCH && + (env->v7m.ccr[M_REG_NS] & R_V7M_CCR_BFHFNMIGN_MASK) && + armv7m_nvic_neg_prio_requested(env->nvic, env->v7m.secure)) { + env->v7m.cfsr[M_REG_NS] |= + (R_V7M_CFSR_PRECISERR_MASK | R_V7M_CFSR_BFARVALID_MASK); + env->v7m.bfar = addr; + return; + } + /* now we have a real cpu fault */ cpu_restore_state(cs, retaddr); -- 2.43.0
From: Nicholas Righi <nicholasrighi@gmail.com> The I2C interrupts are only routed to the legacy interrupt controller. This means that for modern device trees that use the GIC, the interrupts don't work. This patch adds a splitter to route the I2C interrupt to both the legacy interrupt controller and the GIC. Testing Add these lines to QEMU invocation -drive if=none,id=i2c_storage,format=raw,file=eeprom.bin \ -device at24c-eeprom,bus=i2c-bus.1,address=0x50,drive=i2c_storage,rom-size=4096 \ note: eeprom.bin is all zeros Before this change, running i2c get to read from EEPROM would result in this i2cget -y 1 0x50 Error: Read failed After this change, running i2c to read from EEPROM results in this i2cget -y 1 0x50 0x00 The eeprom can now also be enabled in the device tree. Before the eeprom driver load would fail due to the read failing ls -l /sys/bus/i2c/devices/i2c-1/1-0050/ | grep -i eeprom -rw------- 1 root root 4096 May 17 16:57 eeprom Signed-off-by: Nicholas Righi <nicholasrighi@gmail.com> Message-id: 20260609024027.22140-1-nicholasrighi@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/bcm2835_peripherals.c | 9 +++++++++ hw/arm/bcm2838.c | 4 ++++ include/hw/arm/bcm2835_peripherals.h | 2 ++ include/hw/arm/bcm2838_peripherals.h | 1 + 4 files changed, 16 insertions(+) diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -XXX,XX +XXX,XX @@ static void raspi_peripherals_base_init(Object *obj) &s->orgated_i2c_irq, TYPE_OR_IRQ); object_property_set_int(OBJECT(&s->orgated_i2c_irq), "num-lines", ORGATED_I2C_IRQ_COUNT, &error_abort); + object_initialize_child(obj, "orgated-i2c-irq-splitter", + &s->orgated_i2c_irq_splitter, TYPE_SPLIT_IRQ); } static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) @@ -XXX,XX +XXX,XX @@ void bcm_soc_peripherals_common_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[n]), 0, qdev_get_gpio_in(DEVICE(&s->orgated_i2c_irq), n)); } + + qdev_prop_set_uint32(DEVICE(&s->orgated_i2c_irq_splitter), "num-lines", 2); + if (!qdev_realize(DEVICE(&s->orgated_i2c_irq_splitter), NULL, errp)) { + return; + } qdev_connect_gpio_out(DEVICE(&s->orgated_i2c_irq), 0, + qdev_get_gpio_in(DEVICE(&s->orgated_i2c_irq_splitter), 0)); + qdev_connect_gpio_out(DEVICE(&s->orgated_i2c_irq_splitter), 0, qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, INTERRUPT_I2C)); diff --git a/hw/arm/bcm2838.c b/hw/arm/bcm2838.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/bcm2838.c +++ b/hw/arm/bcm2838.c @@ -XXX,XX +XXX,XX @@ static void bcm2838_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->aux), 0, qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_AUX_UART1)); + /* Connect the I2C interrupt to the interrupt controller */ + qdev_connect_gpio_out(DEVICE(&ps_base->orgated_i2c_irq_splitter), 1, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_I2C)); + /* Connect VC mailbox to the interrupt controller */ sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->mboxes), 0, qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_MBOX)); diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/bcm2835_peripherals.h +++ b/include/hw/arm/bcm2835_peripherals.h @@ -XXX,XX +XXX,XX @@ #include "hw/usb/hcd-dwc2.h" #include "hw/ssi/bcm2835_spi.h" #include "hw/i2c/bcm2835_i2c.h" +#include "hw/core/split-irq.h" #include "hw/nvram/bcm2835_otp.h" #include "hw/misc/unimp.h" #include "qom/object.h" @@ -XXX,XX +XXX,XX @@ struct BCMSocPeripheralBaseState { BCM2835SPIState spi[1]; BCM2835I2CState i2c[3]; OrIRQState orgated_i2c_irq; + SplitIRQ orgated_i2c_irq_splitter; BCM2835OTPState otp; UnimplementedDeviceState dbus; UnimplementedDeviceState ave0; diff --git a/include/hw/arm/bcm2838_peripherals.h b/include/hw/arm/bcm2838_peripherals.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/bcm2838_peripherals.h +++ b/include/hw/arm/bcm2838_peripherals.h @@ -XXX,XX +XXX,XX @@ #define GIC_SPI_INTERRUPT_DMA_7_8 87 #define GIC_SPI_INTERRUPT_DMA_9_10 88 #define GIC_SPI_INTERRUPT_AUX_UART1 93 +#define GIC_SPI_INTERRUPT_I2C 117 #define GIC_SPI_INTERRUPT_SDHOST 120 #define GIC_SPI_INTERRUPT_UART0 121 #define GIC_SPI_INTERRUPT_RNG200 125 -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpu-features.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -XXX,XX +XXX,XX @@ static inline bool isar_feature_aa64_sve2(const ARMISARegisters *id) static inline bool isar_feature_aa64_sve2p1(const ARMISARegisters *id) { - return FIELD_EX64_IDREG(id, ID_AA64ZFR0, SVEVER) >=2; + return FIELD_EX64_IDREG(id, ID_AA64ZFR0, SVEVER) >= 2; +} + +static inline bool isar_feature_aa64_sve2p2(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64ZFR0, SVEVER) >= 3; } static inline bool isar_feature_aa64_sve2_aes(const ARMISARegisters *id) @@ -XXX,XX +XXX,XX @@ static inline bool isar_feature_aa64_sme2p1(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64SMFR0, SMEVER) >= 2; } +static inline bool isar_feature_aa64_sme2p2(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64SMFR0, SMEVER) >= 3; +} + static inline bool isar_feature_aa64_f8cvt(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64FPFR0, F8CVT); @@ -XXX,XX +XXX,XX @@ static inline bool isar_feature_aa64_sme2p1_or_sve2p1(const ARMISARegisters *id) return isar_feature_aa64_sme2p1(id) || isar_feature_aa64_sve2p1(id); } +static inline bool isar_feature_aa64_sme2p2_or_sve2p2(const ARMISARegisters *id) +{ + return isar_feature_aa64_sme2p2(id) || isar_feature_aa64_sve2p2(id); +} + static inline bool isar_feature_aa64_sme2_i16i64(const ARMISARegisters *id) { return isar_feature_aa64_sme2(id) && isar_feature_aa64_sme_i16i64(id); -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> Add an "_m" suffix to indicate merging, in preparation for adding new predicated zeroing instructions. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/sve.decode | 152 ++++++++++++++--------------- target/arm/tcg/translate-sve.c | 169 +++++++++++++++++---------------- 2 files changed, 165 insertions(+), 156 deletions(-) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -XXX,XX +XXX,XX @@ LSL_zpzw 00000100 .. 011 011 100 ... ..... ..... @rdn_pg_rm # SVE unary bit operations (predicated) # Note esz != 0 for FABS and FNEG. -CLS 00000100 .. 011 000 101 ... ..... ..... @rd_pg_rn -CLZ 00000100 .. 011 001 101 ... ..... ..... @rd_pg_rn -CNT_zpz 00000100 .. 011 010 101 ... ..... ..... @rd_pg_rn -CNOT 00000100 .. 011 011 101 ... ..... ..... @rd_pg_rn -NOT_zpz 00000100 .. 011 110 101 ... ..... ..... @rd_pg_rn -FABS 00000100 .. 011 100 101 ... ..... ..... @rd_pg_rn -FNEG 00000100 .. 011 101 101 ... ..... ..... @rd_pg_rn +CLS_m 00000100 .. 011 000 101 ... ..... ..... @rd_pg_rn +CLZ_m 00000100 .. 011 001 101 ... ..... ..... @rd_pg_rn +CNT_zpz_m 00000100 .. 011 010 101 ... ..... ..... @rd_pg_rn +CNOT_m 00000100 .. 011 011 101 ... ..... ..... @rd_pg_rn +NOT_zpz_m 00000100 .. 011 110 101 ... ..... ..... @rd_pg_rn +FABS_m 00000100 .. 011 100 101 ... ..... ..... @rd_pg_rn +FNEG_m 00000100 .. 011 101 101 ... ..... ..... @rd_pg_rn # SVE integer unary operations (predicated) # Note esz > original size for extensions. -ABS 00000100 .. 010 110 101 ... ..... ..... @rd_pg_rn -NEG 00000100 .. 010 111 101 ... ..... ..... @rd_pg_rn -SXTB 00000100 .. 010 000 101 ... ..... ..... @rd_pg_rn -UXTB 00000100 .. 010 001 101 ... ..... ..... @rd_pg_rn -SXTH 00000100 .. 010 010 101 ... ..... ..... @rd_pg_rn -UXTH 00000100 .. 010 011 101 ... ..... ..... @rd_pg_rn -SXTW 00000100 .. 010 100 101 ... ..... ..... @rd_pg_rn -UXTW 00000100 .. 010 101 101 ... ..... ..... @rd_pg_rn +ABS_m 00000100 .. 010 110 101 ... ..... ..... @rd_pg_rn +NEG_m 00000100 .. 010 111 101 ... ..... ..... @rd_pg_rn +SXTB_m 00000100 .. 010 000 101 ... ..... ..... @rd_pg_rn +UXTB_m 00000100 .. 010 001 101 ... ..... ..... @rd_pg_rn +SXTH_m 00000100 .. 010 010 101 ... ..... ..... @rd_pg_rn +UXTH_m 00000100 .. 010 011 101 ... ..... ..... @rd_pg_rn +SXTW_m 00000100 .. 010 100 101 ... ..... ..... @rd_pg_rn +UXTW_m 00000100 .. 010 101 101 ... ..... ..... @rd_pg_rn ### SVE Floating Point Compare - Vectors Group @@ -XXX,XX +XXX,XX @@ CPY_m_r 00000101 .. 101000 101 ... ..... ..... @rd_pg_rn # SVE reverse within elements # Note esz >= operation size -REVB 00000101 .. 1001 00 100 ... ..... ..... @rd_pg_rn -REVH 00000101 .. 1001 01 100 ... ..... ..... @rd_pg_rn -REVW 00000101 .. 1001 10 100 ... ..... ..... @rd_pg_rn -RBIT 00000101 .. 1001 11 100 ... ..... ..... @rd_pg_rn -REVD 00000101 00 1011 10 100 ... ..... ..... @rd_pg_rn_e0 +REVB_m 00000101 .. 1001 00 100 ... ..... ..... @rd_pg_rn +REVH_m 00000101 .. 1001 01 100 ... ..... ..... @rd_pg_rn +REVW_m 00000101 .. 1001 10 100 ... ..... ..... @rd_pg_rn +RBIT_m 00000101 .. 1001 11 100 ... ..... ..... @rd_pg_rn +REVD_m 00000101 00 1011 10 100 ... ..... ..... @rd_pg_rn_e0 # SVE vector splice (predicated, destructive) SPLICE 00000101 .. 101 100 100 ... ..... ..... @rdn_pg_rm @@ -XXX,XX +XXX,XX @@ FNMLS_zpzzz 01100101 .. 1 ..... 111 ... ..... ..... @rdn_pg_rm_ra ### SVE FP Unary Operations Predicated Group # SVE floating-point convert precision -FCVT_sh 01100101 10 0010 00 101 ... ..... ..... @rd_pg_rn_e0 -FCVT_hs 01100101 10 0010 01 101 ... ..... ..... @rd_pg_rn_e0 -BFCVT 01100101 10 0010 10 101 ... ..... ..... @rd_pg_rn_e0 -FCVT_dh 01100101 11 0010 00 101 ... ..... ..... @rd_pg_rn_e0 -FCVT_hd 01100101 11 0010 01 101 ... ..... ..... @rd_pg_rn_e0 -FCVT_ds 01100101 11 0010 10 101 ... ..... ..... @rd_pg_rn_e0 -FCVT_sd 01100101 11 0010 11 101 ... ..... ..... @rd_pg_rn_e0 +FCVT_sh_m 01100101 10 0010 00 101 ... ..... ..... @rd_pg_rn_e0 +FCVT_hs_m 01100101 10 0010 01 101 ... ..... ..... @rd_pg_rn_e0 +BFCVT_m 01100101 10 0010 10 101 ... ..... ..... @rd_pg_rn_e0 +FCVT_dh_m 01100101 11 0010 00 101 ... ..... ..... @rd_pg_rn_e0 +FCVT_hd_m 01100101 11 0010 01 101 ... ..... ..... @rd_pg_rn_e0 +FCVT_ds_m 01100101 11 0010 10 101 ... ..... ..... @rd_pg_rn_e0 +FCVT_sd_m 01100101 11 0010 11 101 ... ..... ..... @rd_pg_rn_e0 # SVE floating-point convert to integer -FCVTZS_hh 01100101 01 011 01 0 101 ... ..... ..... @rd_pg_rn_e0 -FCVTZU_hh 01100101 01 011 01 1 101 ... ..... ..... @rd_pg_rn_e0 -FCVTZS_hs 01100101 01 011 10 0 101 ... ..... ..... @rd_pg_rn_e0 -FCVTZU_hs 01100101 01 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 -FCVTZS_hd 01100101 01 011 11 0 101 ... ..... ..... @rd_pg_rn_e0 -FCVTZU_hd 01100101 01 011 11 1 101 ... ..... ..... @rd_pg_rn_e0 -FCVTZS_ss 01100101 10 011 10 0 101 ... ..... ..... @rd_pg_rn_e0 -FCVTZU_ss 01100101 10 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 -FCVTZS_ds 01100101 11 011 00 0 101 ... ..... ..... @rd_pg_rn_e0 -FCVTZU_ds 01100101 11 011 00 1 101 ... ..... ..... @rd_pg_rn_e0 -FCVTZS_sd 01100101 11 011 10 0 101 ... ..... ..... @rd_pg_rn_e0 -FCVTZU_sd 01100101 11 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 -FCVTZS_dd 01100101 11 011 11 0 101 ... ..... ..... @rd_pg_rn_e0 -FCVTZU_dd 01100101 11 011 11 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_hh_m 01100101 01 011 01 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_hh_m 01100101 01 011 01 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_hs_m 01100101 01 011 10 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_hs_m 01100101 01 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_hd_m 01100101 01 011 11 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_hd_m 01100101 01 011 11 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_ss_m 01100101 10 011 10 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_ss_m 01100101 10 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_ds_m 01100101 11 011 00 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_ds_m 01100101 11 011 00 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_sd_m 01100101 11 011 10 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_sd_m 01100101 11 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_dd_m 01100101 11 011 11 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_dd_m 01100101 11 011 11 1 101 ... ..... ..... @rd_pg_rn_e0 # SVE floating-point round to integral value -FRINTN 01100101 .. 000 000 101 ... ..... ..... @rd_pg_rn -FRINTP 01100101 .. 000 001 101 ... ..... ..... @rd_pg_rn -FRINTM 01100101 .. 000 010 101 ... ..... ..... @rd_pg_rn -FRINTZ 01100101 .. 000 011 101 ... ..... ..... @rd_pg_rn -FRINTA 01100101 .. 000 100 101 ... ..... ..... @rd_pg_rn -FRINTX 01100101 .. 000 110 101 ... ..... ..... @rd_pg_rn -FRINTI 01100101 .. 000 111 101 ... ..... ..... @rd_pg_rn +FRINTN_m 01100101 .. 000 000 101 ... ..... ..... @rd_pg_rn +FRINTP_m 01100101 .. 000 001 101 ... ..... ..... @rd_pg_rn +FRINTM_m 01100101 .. 000 010 101 ... ..... ..... @rd_pg_rn +FRINTZ_m 01100101 .. 000 011 101 ... ..... ..... @rd_pg_rn +FRINTA_m 01100101 .. 000 100 101 ... ..... ..... @rd_pg_rn +FRINTX_m 01100101 .. 000 110 101 ... ..... ..... @rd_pg_rn +FRINTI_m 01100101 .. 000 111 101 ... ..... ..... @rd_pg_rn # SVE floating-point unary operations -FRECPX 01100101 .. 001 100 101 ... ..... ..... @rd_pg_rn -FSQRT 01100101 .. 001 101 101 ... ..... ..... @rd_pg_rn +FRECPX_m 01100101 .. 001 100 101 ... ..... ..... @rd_pg_rn +FSQRT_m 01100101 .. 001 101 101 ... ..... ..... @rd_pg_rn # SVE integer convert to floating-point -SCVTF_hh 01100101 01 010 01 0 101 ... ..... ..... @rd_pg_rn_e0 -SCVTF_sh 01100101 01 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 -SCVTF_dh 01100101 01 010 11 0 101 ... ..... ..... @rd_pg_rn_e0 -SCVTF_ss 01100101 10 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 -SCVTF_sd 01100101 11 010 00 0 101 ... ..... ..... @rd_pg_rn_e0 -SCVTF_ds 01100101 11 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 -SCVTF_dd 01100101 11 010 11 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_hh_m 01100101 01 010 01 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_sh_m 01100101 01 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_dh_m 01100101 01 010 11 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_ss_m 01100101 10 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_sd_m 01100101 11 010 00 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_ds_m 01100101 11 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_dd_m 01100101 11 010 11 0 101 ... ..... ..... @rd_pg_rn_e0 -UCVTF_hh 01100101 01 010 01 1 101 ... ..... ..... @rd_pg_rn_e0 -UCVTF_sh 01100101 01 010 10 1 101 ... ..... ..... @rd_pg_rn_e0 -UCVTF_dh 01100101 01 010 11 1 101 ... ..... ..... @rd_pg_rn_e0 -UCVTF_ss 01100101 10 010 10 1 101 ... ..... ..... @rd_pg_rn_e0 -UCVTF_sd 01100101 11 010 00 1 101 ... ..... ..... @rd_pg_rn_e0 -UCVTF_ds 01100101 11 010 10 1 101 ... ..... ..... @rd_pg_rn_e0 -UCVTF_dd 01100101 11 010 11 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_hh_m 01100101 01 010 01 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_sh_m 01100101 01 010 10 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_dh_m 01100101 01 010 11 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_ss_m 01100101 10 010 10 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_sd_m 01100101 11 010 00 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_ds_m 01100101 11 010 10 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_dd_m 01100101 11 010 11 1 101 ... ..... ..... @rd_pg_rn_e0 ### SVE Memory - 32-bit Gather and Unsized Contiguous Group @@ -XXX,XX +XXX,XX @@ UADALP_zpzz 01000100 .. 000 101 101 ... ..... ..... @rdm_pg_rn ### SVE2 integer unary operations (predicated) -URECPE 01000100 .. 000 000 101 ... ..... ..... @rd_pg_rn -URSQRTE 01000100 .. 000 001 101 ... ..... ..... @rd_pg_rn -SQABS 01000100 .. 001 000 101 ... ..... ..... @rd_pg_rn -SQNEG 01000100 .. 001 001 101 ... ..... ..... @rd_pg_rn +URECPE_m 01000100 .. 000 000 101 ... ..... ..... @rd_pg_rn +URSQRTE_m 01000100 .. 000 001 101 ... ..... ..... @rd_pg_rn +SQABS_m 01000100 .. 001 000 101 ... ..... ..... @rd_pg_rn +SQNEG_m 01000100 .. 001 001 101 ... ..... ..... @rd_pg_rn ### SVE2 saturating/rounding bitwise shift left (predicated) @@ -XXX,XX +XXX,XX @@ SM4EKEY 01000101 00 1 ..... 11110 0 ..... ..... @rd_rn_rm_e0 RAX1 01000101 00 1 ..... 11110 1 ..... ..... @rd_rn_rm_e0 ### SVE2 floating-point convert precision odd elements -FCVTXNT_ds 01100100 00 0010 10 101 ... ..... ..... @rd_pg_rn_e0 -FCVTX_ds 01100101 00 0010 10 101 ... ..... ..... @rd_pg_rn_e0 -FCVTNT_sh 01100100 10 0010 00 101 ... ..... ..... @rd_pg_rn_e0 -BFCVTNT 01100100 10 0010 10 101 ... ..... ..... @rd_pg_rn_e0 -FCVTLT_hs 01100100 10 0010 01 101 ... ..... ..... @rd_pg_rn_e0 -FCVTNT_ds 01100100 11 0010 10 101 ... ..... ..... @rd_pg_rn_e0 -FCVTLT_sd 01100100 11 0010 11 101 ... ..... ..... @rd_pg_rn_e0 +FCVTXNT_ds_m 01100100 00 0010 10 101 ... ..... ..... @rd_pg_rn_e0 +FCVTX_ds_m 01100101 00 0010 10 101 ... ..... ..... @rd_pg_rn_e0 +FCVTNT_sh_m 01100100 10 0010 00 101 ... ..... ..... @rd_pg_rn_e0 +BFCVTNT_m 01100100 10 0010 10 101 ... ..... ..... @rd_pg_rn_e0 +FCVTLT_hs_m 01100100 10 0010 01 101 ... ..... ..... @rd_pg_rn_e0 +FCVTNT_ds_m 01100100 11 0010 10 101 ... ..... ..... @rd_pg_rn_e0 +FCVTLT_sd_m 01100100 11 0010 11 101 ... ..... ..... @rd_pg_rn_e0 ### SVE2 floating-point convert to integer -FLOGB 01100101 00 011 esz:2 0101 pg:3 rn:5 rd:5 &rpr_esz +FLOGB_m 01100101 00 011 esz:2 0101 pg:3 rn:5 rd:5 &rpr_esz ### SVE2 floating-point multiply-add long (vectors) FMLALB_zzzw 01100100 10 1 ..... 10 0 00 0 ..... ..... @rda_rn_rm_ex esz=2 diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -XXX,XX +XXX,XX @@ TRANS_FEAT(SEL_zpzz, aa64_sme_or_sve, do_sel_z, a->rd, a->rn, a->rm, a->pg, a->e }; \ TRANS_FEAT(NAME, FEAT, gen_gvec_ool_arg_zpz, name##_fns[a->esz], a, 0) -DO_ZPZ(CLS, aa64_sme_or_sve, sve_cls) -DO_ZPZ(CLZ, aa64_sme_or_sve, sve_clz) -DO_ZPZ(CNT_zpz, aa64_sme_or_sve, sve_cnt_zpz) -DO_ZPZ(CNOT, aa64_sme_or_sve, sve_cnot) -DO_ZPZ(NOT_zpz, aa64_sme_or_sve, sve_not_zpz) -DO_ZPZ(ABS, aa64_sme_or_sve, sve_abs) -DO_ZPZ(NEG, aa64_sme_or_sve, sve_neg) -DO_ZPZ(RBIT, aa64_sme_or_sve, sve_rbit) +DO_ZPZ(CLS_m, aa64_sme_or_sve, sve_cls) +DO_ZPZ(CLZ_m, aa64_sme_or_sve, sve_clz) +DO_ZPZ(CNT_zpz_m, aa64_sme_or_sve, sve_cnt_zpz) +DO_ZPZ(CNOT_m, aa64_sme_or_sve, sve_cnot) +DO_ZPZ(NOT_zpz_m, aa64_sme_or_sve, sve_not_zpz) +DO_ZPZ(ABS_m, aa64_sme_or_sve, sve_abs) +DO_ZPZ(NEG_m, aa64_sme_or_sve, sve_neg) +DO_ZPZ(RBIT_m, aa64_sme_or_sve, sve_rbit) DO_ZPZ(ORQV, aa64_sme2p1_or_sve2p1, sve2p1_orqv) DO_ZPZ(EORQV, aa64_sme2p1_or_sve2p1, sve2p1_eorqv) DO_ZPZ(ANDQV, aa64_sme2p1_or_sve2p1, sve2p1_andqv) @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3 * const fabs_ah_fns[4] = { NULL, gen_helper_sve_ah_fabs_h, gen_helper_sve_ah_fabs_s, gen_helper_sve_ah_fabs_d, }; -TRANS_FEAT(FABS, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, +TRANS_FEAT(FABS_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, s->fpcr_ah ? fabs_ah_fns[a->esz] : fabs_fns[a->esz], a, 0) static gen_helper_gvec_3 * const fneg_fns[4] = { @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3 * const fneg_ah_fns[4] = { NULL, gen_helper_sve_ah_fneg_h, gen_helper_sve_ah_fneg_s, gen_helper_sve_ah_fneg_d, }; -TRANS_FEAT(FNEG, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, +TRANS_FEAT(FNEG_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, s->fpcr_ah ? fneg_ah_fns[a->esz] : fneg_fns[a->esz], a, 0) static gen_helper_gvec_3 * const sxtb_fns[4] = { NULL, gen_helper_sve_sxtb_h, gen_helper_sve_sxtb_s, gen_helper_sve_sxtb_d, }; -TRANS_FEAT(SXTB, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sxtb_fns[a->esz], a, 0) +TRANS_FEAT(SXTB_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, + sxtb_fns[a->esz], a, 0) static gen_helper_gvec_3 * const uxtb_fns[4] = { NULL, gen_helper_sve_uxtb_h, gen_helper_sve_uxtb_s, gen_helper_sve_uxtb_d, }; -TRANS_FEAT(UXTB, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, uxtb_fns[a->esz], a, 0) +TRANS_FEAT(UXTB_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, + uxtb_fns[a->esz], a, 0) static gen_helper_gvec_3 * const sxth_fns[4] = { NULL, NULL, gen_helper_sve_sxth_s, gen_helper_sve_sxth_d }; -TRANS_FEAT(SXTH, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sxth_fns[a->esz], a, 0) +TRANS_FEAT(SXTH_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, + sxth_fns[a->esz], a, 0) static gen_helper_gvec_3 * const uxth_fns[4] = { NULL, NULL, gen_helper_sve_uxth_s, gen_helper_sve_uxth_d }; -TRANS_FEAT(UXTH, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, uxth_fns[a->esz], a, 0) +TRANS_FEAT(UXTH_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, + uxth_fns[a->esz], a, 0) -TRANS_FEAT(SXTW, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, +TRANS_FEAT(SXTW_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, a->esz == 3 ? gen_helper_sve_sxtw_d : NULL, a, 0) -TRANS_FEAT(UXTW, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, +TRANS_FEAT(UXTW_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, a->esz == 3 ? gen_helper_sve_uxtw_d : NULL, a, 0) static gen_helper_gvec_3 * const addqv_fns[4] = { @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3 * const revb_fns[4] = { NULL, gen_helper_sve_revb_h, gen_helper_sve_revb_s, gen_helper_sve_revb_d, }; -TRANS_FEAT(REVB, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, revb_fns[a->esz], a, 0) +TRANS_FEAT(REVB_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, + revb_fns[a->esz], a, 0) static gen_helper_gvec_3 * const revh_fns[4] = { NULL, NULL, gen_helper_sve_revh_s, gen_helper_sve_revh_d, }; -TRANS_FEAT(REVH, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, revh_fns[a->esz], a, 0) +TRANS_FEAT(REVH_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, + revh_fns[a->esz], a, 0) -TRANS_FEAT(REVW, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, +TRANS_FEAT(REVW_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, a->esz == 3 ? gen_helper_sve_revw_d : NULL, a, 0) -TRANS_FEAT(REVD, aa64_sme_or_sve2p1, gen_gvec_ool_arg_zpz, +TRANS_FEAT(REVD_m, aa64_sme_or_sve2p1, gen_gvec_ool_arg_zpz, gen_helper_sme_revd_q, a, 0) TRANS_FEAT(SPLICE, aa64_sme_or_sve, gen_gvec_ool_arg_zpzz, @@ -XXX,XX +XXX,XX @@ TRANS_FEAT(FCMLA_zzxz, aa64_sme_or_sve, gen_gvec_fpst_zzzz, fcmla_idx_fns[a->esz *** SVE Floating Point Unary Operations Predicated Group */ -TRANS_FEAT(FCVT_sh, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVT_sh_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_sh, a, 0, FPST_A64) -TRANS_FEAT(FCVT_hs, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVT_hs_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_hs, a, 0, FPST_A64_F16) -TRANS_FEAT(BFCVT, aa64_sme_sve_bf16, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(BFCVT_m, aa64_sme_sve_bf16, gen_gvec_fpst_arg_zpz, gen_helper_sve_bfcvt, a, 0, s->fpcr_ah ? FPST_AH : FPST_A64) -TRANS_FEAT(FCVT_dh, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVT_dh_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_dh, a, 0, FPST_A64) -TRANS_FEAT(FCVT_hd, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVT_hd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_hd, a, 0, FPST_A64_F16) -TRANS_FEAT(FCVT_ds, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVT_ds_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_ds, a, 0, FPST_A64) -TRANS_FEAT(FCVT_sd, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVT_sd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_sd, a, 0, FPST_A64) -TRANS_FEAT(FCVTZS_hh, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTZS_hh_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzs_hh, a, 0, FPST_A64_F16) -TRANS_FEAT(FCVTZU_hh, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTZU_hh_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzu_hh, a, 0, FPST_A64_F16) -TRANS_FEAT(FCVTZS_hs, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTZS_hs_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzs_hs, a, 0, FPST_A64_F16) -TRANS_FEAT(FCVTZU_hs, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTZU_hs_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzu_hs, a, 0, FPST_A64_F16) -TRANS_FEAT(FCVTZS_hd, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTZS_hd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzs_hd, a, 0, FPST_A64_F16) -TRANS_FEAT(FCVTZU_hd, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTZU_hd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzu_hd, a, 0, FPST_A64_F16) -TRANS_FEAT(FCVTZS_ss, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTZS_ss_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzs_ss, a, 0, FPST_A64) -TRANS_FEAT(FCVTZU_ss, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTZU_ss_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzu_ss, a, 0, FPST_A64) -TRANS_FEAT(FCVTZS_sd, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTZS_sd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzs_sd, a, 0, FPST_A64) -TRANS_FEAT(FCVTZU_sd, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTZU_sd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzu_sd, a, 0, FPST_A64) -TRANS_FEAT(FCVTZS_ds, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTZS_ds_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzs_ds, a, 0, FPST_A64) -TRANS_FEAT(FCVTZU_ds, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTZU_ds_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzu_ds, a, 0, FPST_A64) -TRANS_FEAT(FCVTZS_dd, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTZS_dd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzs_dd, a, 0, FPST_A64) -TRANS_FEAT(FCVTZU_dd, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTZU_dd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzu_dd, a, 0, FPST_A64) static gen_helper_gvec_3_ptr * const frint_fns[] = { @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3_ptr * const frint_fns[] = { gen_helper_sve_frint_s, gen_helper_sve_frint_d }; -TRANS_FEAT(FRINTI, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, frint_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) +TRANS_FEAT(FRINTI_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, + frint_fns[a->esz], a, 0, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) static gen_helper_gvec_3_ptr * const frintx_fns[] = { NULL, @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3_ptr * const frintx_fns[] = { gen_helper_sve_frintx_s, gen_helper_sve_frintx_d }; -TRANS_FEAT(FRINTX, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, frintx_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); +TRANS_FEAT(FRINTX_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, + frintx_fns[a->esz], a, 0, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); static bool do_frint_mode(DisasContext *s, arg_rpr_esz *a, ARMFPRounding mode, gen_helper_gvec_3_ptr *fn) @@ -XXX,XX +XXX,XX @@ static bool do_frint_mode(DisasContext *s, arg_rpr_esz *a, return true; } -TRANS_FEAT(FRINTN, aa64_sme_or_sve, do_frint_mode, a, +TRANS_FEAT(FRINTN_m, aa64_sme_or_sve, do_frint_mode, a, FPROUNDING_TIEEVEN, frint_fns[a->esz]) -TRANS_FEAT(FRINTP, aa64_sme_or_sve, do_frint_mode, a, +TRANS_FEAT(FRINTP_m, aa64_sme_or_sve, do_frint_mode, a, FPROUNDING_POSINF, frint_fns[a->esz]) -TRANS_FEAT(FRINTM, aa64_sme_or_sve, do_frint_mode, a, +TRANS_FEAT(FRINTM_m, aa64_sme_or_sve, do_frint_mode, a, FPROUNDING_NEGINF, frint_fns[a->esz]) -TRANS_FEAT(FRINTZ, aa64_sme_or_sve, do_frint_mode, a, +TRANS_FEAT(FRINTZ_m, aa64_sme_or_sve, do_frint_mode, a, FPROUNDING_ZERO, frint_fns[a->esz]) -TRANS_FEAT(FRINTA, aa64_sme_or_sve, do_frint_mode, a, +TRANS_FEAT(FRINTA_m, aa64_sme_or_sve, do_frint_mode, a, FPROUNDING_TIEAWAY, frint_fns[a->esz]) static gen_helper_gvec_3_ptr * const frecpx_fns[] = { NULL, gen_helper_sve_frecpx_h, gen_helper_sve_frecpx_s, gen_helper_sve_frecpx_d, }; -TRANS_FEAT(FRECPX, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, frecpx_fns[a->esz], - a, 0, select_ah_fpst(s, a->esz)) +TRANS_FEAT(FRECPX_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, + frecpx_fns[a->esz], a, 0, select_ah_fpst(s, a->esz)) static gen_helper_gvec_3_ptr * const fsqrt_fns[] = { NULL, gen_helper_sve_fsqrt_h, gen_helper_sve_fsqrt_s, gen_helper_sve_fsqrt_d, }; -TRANS_FEAT(FSQRT, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, fsqrt_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) +TRANS_FEAT(FSQRT_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, + fsqrt_fns[a->esz], a, 0, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) -TRANS_FEAT(SCVTF_hh, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(SCVTF_hh_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_scvt_hh, a, 0, FPST_A64_F16) -TRANS_FEAT(SCVTF_sh, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(SCVTF_sh_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_scvt_sh, a, 0, FPST_A64_F16) -TRANS_FEAT(SCVTF_dh, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(SCVTF_dh_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_scvt_dh, a, 0, FPST_A64_F16) -TRANS_FEAT(SCVTF_ss, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(SCVTF_ss_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_scvt_ss, a, 0, FPST_A64) -TRANS_FEAT(SCVTF_ds, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(SCVTF_ds_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_scvt_ds, a, 0, FPST_A64) -TRANS_FEAT(SCVTF_sd, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(SCVTF_sd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_scvt_sd, a, 0, FPST_A64) -TRANS_FEAT(SCVTF_dd, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(SCVTF_dd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_scvt_dd, a, 0, FPST_A64) -TRANS_FEAT(UCVTF_hh, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(UCVTF_hh_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_ucvt_hh, a, 0, FPST_A64_F16) -TRANS_FEAT(UCVTF_sh, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(UCVTF_sh_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_ucvt_sh, a, 0, FPST_A64_F16) -TRANS_FEAT(UCVTF_dh, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(UCVTF_dh_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_ucvt_dh, a, 0, FPST_A64_F16) -TRANS_FEAT(UCVTF_ss, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(UCVTF_ss_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_ucvt_ss, a, 0, FPST_A64) -TRANS_FEAT(UCVTF_ds, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(UCVTF_ds_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_ucvt_ds, a, 0, FPST_A64) -TRANS_FEAT(UCVTF_sd, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(UCVTF_sd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_ucvt_sd, a, 0, FPST_A64) -TRANS_FEAT(UCVTF_dd, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(UCVTF_dd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_ucvt_dd, a, 0, FPST_A64) /* @@ -XXX,XX +XXX,XX @@ TRANS_FEAT(UADALP_zpzz, aa64_sme_or_sve2, gen_gvec_ool_arg_zpzz, * SVE2 integer unary operations (predicated) */ -TRANS_FEAT(URECPE, aa64_sme_or_sve2, gen_gvec_ool_arg_zpz, +TRANS_FEAT(URECPE_m, aa64_sme_or_sve2, gen_gvec_ool_arg_zpz, a->esz == 2 ? gen_helper_sve2_urecpe_s : NULL, a, 0) -TRANS_FEAT(URSQRTE, aa64_sme_or_sve2, gen_gvec_ool_arg_zpz, +TRANS_FEAT(URSQRTE_m, aa64_sme_or_sve2, gen_gvec_ool_arg_zpz, a->esz == 2 ? gen_helper_sve2_ursqrte_s : NULL, a, 0) static gen_helper_gvec_3 * const sqabs_fns[4] = { gen_helper_sve2_sqabs_b, gen_helper_sve2_sqabs_h, gen_helper_sve2_sqabs_s, gen_helper_sve2_sqabs_d, }; -TRANS_FEAT(SQABS, aa64_sme_or_sve2, gen_gvec_ool_arg_zpz, sqabs_fns[a->esz], a, 0) +TRANS_FEAT(SQABS_m, aa64_sme_or_sve2, gen_gvec_ool_arg_zpz, sqabs_fns[a->esz], a, 0) static gen_helper_gvec_3 * const sqneg_fns[4] = { gen_helper_sve2_sqneg_b, gen_helper_sve2_sqneg_h, gen_helper_sve2_sqneg_s, gen_helper_sve2_sqneg_d, }; -TRANS_FEAT(SQNEG, aa64_sme_or_sve2, gen_gvec_ool_arg_zpz, sqneg_fns[a->esz], a, 0) +TRANS_FEAT(SQNEG_m, aa64_sme_or_sve2, gen_gvec_ool_arg_zpz, sqneg_fns[a->esz], a, 0) DO_ZPZZ(SQSHL, aa64_sme_or_sve2, sve2_sqshl) DO_ZPZZ(SQRSHL, aa64_sme_or_sve2, sve2_sqrshl) @@ -XXX,XX +XXX,XX @@ static bool trans_RAX1(DisasContext *s, arg_RAX1 *a) return gen_gvec_fn_arg_zzz(s, gen_gvec_rax1, a); } -TRANS_FEAT(FCVTNT_sh, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTNT_sh_m, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, gen_helper_sve2_fcvtnt_sh, a, 0, FPST_A64) -TRANS_FEAT(FCVTNT_ds, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTNT_ds_m, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, gen_helper_sve2_fcvtnt_ds, a, 0, FPST_A64) -TRANS_FEAT(BFCVTNT, aa64_sme_sve_bf16, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(BFCVTNT_m, aa64_sme_sve_bf16, gen_gvec_fpst_arg_zpz, gen_helper_sve_bfcvtnt, a, 0, s->fpcr_ah ? FPST_AH : FPST_A64) -TRANS_FEAT(FCVTLT_hs, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTLT_hs_m, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, gen_helper_sve2_fcvtlt_hs, a, 0, FPST_A64_F16) -TRANS_FEAT(FCVTLT_sd, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, +TRANS_FEAT(FCVTLT_sd_m, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, gen_helper_sve2_fcvtlt_sd, a, 0, FPST_A64) -TRANS_FEAT(FCVTX_ds, aa64_sme_or_sve2, do_frint_mode, a, +TRANS_FEAT(FCVTX_ds_m, aa64_sme_or_sve2, do_frint_mode, a, FPROUNDING_ODD, gen_helper_sve_fcvt_ds) -TRANS_FEAT(FCVTXNT_ds, aa64_sme_or_sve2, do_frint_mode, a, +TRANS_FEAT(FCVTXNT_ds_m, aa64_sme_or_sve2, do_frint_mode, a, FPROUNDING_ODD, gen_helper_sve2_fcvtnt_ds) static gen_helper_gvec_3_ptr * const flogb_fns[] = { NULL, gen_helper_flogb_h, gen_helper_flogb_s, gen_helper_flogb_d }; -TRANS_FEAT(FLOGB, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, flogb_fns[a->esz], +TRANS_FEAT(FLOGB_m, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, flogb_fns[a->esz], a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) static bool do_FMLAL_zzzw(DisasContext *s, arg_rrrr_esz *a, bool sub, bool sel) -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> Use the low bit of simd_data to hold a 'zeroing' bit. The simd_data field is currently unused and always 0. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/sve_helper.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -XXX,XX +XXX,XX @@ DO_ZPZW(sve_lsl_zpzw_s, uint32_t, uint64_t, H1_4, DO_LSL) #undef DO_ZPZW -/* Fully general two-operand expander, controlled by a predicate. - */ +/* Fully general two-operand expander, controlled by a predicate. */ #define DO_ZPZ(NAME, TYPE, H, OP) \ void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \ { \ intptr_t i, opr_sz = simd_oprsz(desc); \ + bool zeroing = simd_data(desc) & 1; \ for (i = 0; i < opr_sz; ) { \ uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ do { \ if (pg & 1) { \ TYPE nn = *(TYPE *)(vn + H(i)); \ *(TYPE *)(vd + H(i)) = OP(nn); \ + } else if (zeroing) { \ + *(TYPE *)(vd + H(i)) = 0; \ } \ i += sizeof(TYPE), pg >>= sizeof(TYPE); \ } while (i & 15); \ @@ -XXX,XX +XXX,XX @@ void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \ void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \ { \ intptr_t i, opr_sz = simd_oprsz(desc) / 8; \ + bool zeroing = simd_data(desc) & 1; \ TYPE *d = vd, *n = vn; \ uint8_t *pg = vg; \ for (i = 0; i < opr_sz; i += 1) { \ if (pg[H1(i)] & 1) { \ TYPE nn = n[i]; \ d[i] = OP(nn); \ + } else if (zeroing) { \ + d[i] = 0; \ } \ } \ } @@ -XXX,XX +XXX,XX @@ DO_ZPZS_FP(sve_ah_fmins_h, float16, H1_2, helper_vfp_ah_minh) DO_ZPZS_FP(sve_ah_fmins_s, float32, H1_4, helper_vfp_ah_mins) DO_ZPZS_FP(sve_ah_fmins_d, float64, H1_8, helper_vfp_ah_mind) -/* Fully general two-operand expander, controlled by a predicate, +/* + * Fully general two-operand expander, controlled by a predicate, * With the extra float_status parameter. */ #define DO_ZPZ_FP(NAME, TYPE, H, OP) \ @@ -XXX,XX +XXX,XX @@ void HELPER(NAME)(void *vd, void *vn, void *vg, \ float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc); \ + bool zeroing = simd_data(desc) & 1; \ uint64_t *g = vg; \ do { \ uint64_t pg = g[(i - 1) >> 6]; \ @@ -XXX,XX +XXX,XX @@ void HELPER(NAME)(void *vd, void *vn, void *vg, \ if (likely((pg >> (i & 63)) & 1)) { \ TYPE nn = *(TYPE *)(vn + H(i)); \ *(TYPE *)(vd + H(i)) = OP(nn, status); \ + } else if (zeroing) { \ + *(TYPE *)(vd + H(i)) = 0; \ } \ } while (i & 63); \ } while (i != 0); \ -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> Prepare for adding zeroing instructions for some of these. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/translate-sve.c | 84 +++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 17 deletions(-) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -XXX,XX +XXX,XX @@ TRANS_FEAT(SEL_zpzz, aa64_sme_or_sve, do_sel_z, a->rd, a->rn, a->rm, a->pg, a->e *** SVE Integer Arithmetic - Unary Predicated Group */ -#define DO_ZPZ(NAME, FEAT, name) \ - static gen_helper_gvec_3 * const name##_fns[4] = { \ - gen_helper_##name##_b, gen_helper_##name##_h, \ - gen_helper_##name##_s, gen_helper_##name##_d, \ - }; \ - TRANS_FEAT(NAME, FEAT, gen_gvec_ool_arg_zpz, name##_fns[a->esz], a, 0) +static gen_helper_gvec_3 * const sve_cls_fns[4] = { + gen_helper_sve_cls_b, gen_helper_sve_cls_h, + gen_helper_sve_cls_s, gen_helper_sve_cls_d, +}; +TRANS_FEAT(CLS_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sve_cls_fns[a->esz], a, 0) -DO_ZPZ(CLS_m, aa64_sme_or_sve, sve_cls) -DO_ZPZ(CLZ_m, aa64_sme_or_sve, sve_clz) -DO_ZPZ(CNT_zpz_m, aa64_sme_or_sve, sve_cnt_zpz) -DO_ZPZ(CNOT_m, aa64_sme_or_sve, sve_cnot) -DO_ZPZ(NOT_zpz_m, aa64_sme_or_sve, sve_not_zpz) -DO_ZPZ(ABS_m, aa64_sme_or_sve, sve_abs) -DO_ZPZ(NEG_m, aa64_sme_or_sve, sve_neg) -DO_ZPZ(RBIT_m, aa64_sme_or_sve, sve_rbit) -DO_ZPZ(ORQV, aa64_sme2p1_or_sve2p1, sve2p1_orqv) -DO_ZPZ(EORQV, aa64_sme2p1_or_sve2p1, sve2p1_eorqv) -DO_ZPZ(ANDQV, aa64_sme2p1_or_sve2p1, sve2p1_andqv) +static gen_helper_gvec_3 * const sve_clz_fns[4] = { + gen_helper_sve_clz_b, gen_helper_sve_clz_h, + gen_helper_sve_clz_s, gen_helper_sve_clz_d, +}; +TRANS_FEAT(CLZ_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sve_clz_fns[a->esz], a, 0) + +static gen_helper_gvec_3 * const sve_cnt_zpz_fns[4] = { + gen_helper_sve_cnt_zpz_b, gen_helper_sve_cnt_zpz_h, + gen_helper_sve_cnt_zpz_s, gen_helper_sve_cnt_zpz_d, +}; +TRANS_FEAT(CNT_zpz_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sve_cnt_zpz_fns[a->esz], a, 0) + +static gen_helper_gvec_3 * const sve_cnot_fns[4] = { + gen_helper_sve_cnot_b, gen_helper_sve_cnot_h, + gen_helper_sve_cnot_s, gen_helper_sve_cnot_d, +}; +TRANS_FEAT(CNOT_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sve_cnot_fns[a->esz], a, 0) + +static gen_helper_gvec_3 * const sve_not_zpz_fns[4] = { + gen_helper_sve_not_zpz_b, gen_helper_sve_not_zpz_h, + gen_helper_sve_not_zpz_s, gen_helper_sve_not_zpz_d, +}; +TRANS_FEAT(NOT_zpz_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sve_not_zpz_fns[a->esz], a, 0) + +static gen_helper_gvec_3 * const sve_abs_fns[4] = { + gen_helper_sve_abs_b, gen_helper_sve_abs_h, + gen_helper_sve_abs_s, gen_helper_sve_abs_d, +}; +TRANS_FEAT(ABS_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sve_abs_fns[a->esz], a, 0) + +static gen_helper_gvec_3 * const sve_neg_fns[4] = { + gen_helper_sve_neg_b, gen_helper_sve_neg_h, + gen_helper_sve_neg_s, gen_helper_sve_neg_d, +}; +TRANS_FEAT(NEG_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sve_neg_fns[a->esz], a, 0) + +static gen_helper_gvec_3 * const sve_rbit_fns[4] = { + gen_helper_sve_rbit_b, gen_helper_sve_rbit_h, + gen_helper_sve_rbit_s, gen_helper_sve_rbit_d, +}; +TRANS_FEAT(RBIT_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sve_rbit_fns[a->esz], a, 0) + +static gen_helper_gvec_3 * const sve2p1_orqv_fns[4] = { + gen_helper_sve2p1_orqv_b, gen_helper_sve2p1_orqv_h, + gen_helper_sve2p1_orqv_s, gen_helper_sve2p1_orqv_d, +}; +TRANS_FEAT(ORQV, aa64_sme2p1_or_sve2p1, gen_gvec_ool_arg_zpz, + sve2p1_orqv_fns[a->esz], a, 0) + +static gen_helper_gvec_3 * const sve2p1_eorqv_fns[4] = { + gen_helper_sve2p1_eorqv_b, gen_helper_sve2p1_eorqv_h, + gen_helper_sve2p1_eorqv_s, gen_helper_sve2p1_eorqv_d, +}; +TRANS_FEAT(EORQV, aa64_sme2p1_or_sve2p1, gen_gvec_ool_arg_zpz, + sve2p1_eorqv_fns[a->esz], a, 0) + +static gen_helper_gvec_3 * const sve2p1_andqv_fns[4] = { + gen_helper_sve2p1_andqv_b, gen_helper_sve2p1_andqv_h, + gen_helper_sve2p1_andqv_s, gen_helper_sve2p1_andqv_d, +}; +TRANS_FEAT(ANDQV, aa64_sme2p1_or_sve2p1, gen_gvec_ool_arg_zpz, + sve2p1_andqv_fns[a->esz], a, 0) static gen_helper_gvec_3 * const fabs_fns[4] = { NULL, gen_helper_sve_fabs_h, -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> This includes ABS, NEG, SXT{B,H,W}. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/sve.decode | 9 +++++++++ target/arm/tcg/translate-sve.c | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -XXX,XX +XXX,XX @@ UXTH_m 00000100 .. 010 011 101 ... ..... ..... @rd_pg_rn SXTW_m 00000100 .. 010 100 101 ... ..... ..... @rd_pg_rn UXTW_m 00000100 .. 010 101 101 ... ..... ..... @rd_pg_rn +ABS_z 00000100 .. 000 110 101 ... ..... ..... @rd_pg_rn +NEG_z 00000100 .. 000 111 101 ... ..... ..... @rd_pg_rn +SXTB_z 00000100 .. 000 000 101 ... ..... ..... @rd_pg_rn +UXTB_z 00000100 .. 000 001 101 ... ..... ..... @rd_pg_rn +SXTH_z 00000100 .. 000 010 101 ... ..... ..... @rd_pg_rn +UXTH_z 00000100 .. 000 011 101 ... ..... ..... @rd_pg_rn +SXTW_z 00000100 .. 000 100 101 ... ..... ..... @rd_pg_rn +UXTW_z 00000100 .. 000 101 101 ... ..... ..... @rd_pg_rn + ### SVE Floating Point Compare - Vectors Group # SVE floating-point compare vectors diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3 * const sve_abs_fns[4] = { gen_helper_sve_abs_s, gen_helper_sve_abs_d, }; TRANS_FEAT(ABS_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sve_abs_fns[a->esz], a, 0) +TRANS_FEAT(ABS_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, sve_abs_fns[a->esz], a, 1) static gen_helper_gvec_3 * const sve_neg_fns[4] = { gen_helper_sve_neg_b, gen_helper_sve_neg_h, gen_helper_sve_neg_s, gen_helper_sve_neg_d, }; TRANS_FEAT(NEG_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sve_neg_fns[a->esz], a, 0) +TRANS_FEAT(NEG_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, sve_neg_fns[a->esz], a, 1) static gen_helper_gvec_3 * const sve_rbit_fns[4] = { gen_helper_sve_rbit_b, gen_helper_sve_rbit_h, @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3 * const sxtb_fns[4] = { }; TRANS_FEAT(SXTB_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sxtb_fns[a->esz], a, 0) +TRANS_FEAT(SXTB_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + sxtb_fns[a->esz], a, 1) static gen_helper_gvec_3 * const uxtb_fns[4] = { NULL, gen_helper_sve_uxtb_h, @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3 * const uxtb_fns[4] = { }; TRANS_FEAT(UXTB_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, uxtb_fns[a->esz], a, 0) +TRANS_FEAT(UXTB_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + uxtb_fns[a->esz], a, 1) static gen_helper_gvec_3 * const sxth_fns[4] = { NULL, NULL, gen_helper_sve_sxth_s, gen_helper_sve_sxth_d }; TRANS_FEAT(SXTH_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sxth_fns[a->esz], a, 0) +TRANS_FEAT(SXTH_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + sxth_fns[a->esz], a, 1) static gen_helper_gvec_3 * const uxth_fns[4] = { NULL, NULL, gen_helper_sve_uxth_s, gen_helper_sve_uxth_d }; TRANS_FEAT(UXTH_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, uxth_fns[a->esz], a, 0) +TRANS_FEAT(UXTH_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + uxth_fns[a->esz], a, 1) TRANS_FEAT(SXTW_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, a->esz == 3 ? gen_helper_sve_sxtw_d : NULL, a, 0) +TRANS_FEAT(SXTW_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + a->esz == 3 ? gen_helper_sve_sxtw_d : NULL, a, 1) + TRANS_FEAT(UXTW_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, a->esz == 3 ? gen_helper_sve_uxtw_d : NULL, a, 0) +TRANS_FEAT(UXTW_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + a->esz == 3 ? gen_helper_sve_uxtw_d : NULL, a, 1) static gen_helper_gvec_3 * const addqv_fns[4] = { gen_helper_sve2p1_addqv_b, gen_helper_sve2p1_addqv_h, -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> This includes CLS, CLZ, CNT, CNOT, NOT, FABS, FNEG. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/sve.decode | 8 ++++++++ target/arm/tcg/translate-sve.c | 21 ++++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -XXX,XX +XXX,XX @@ NOT_zpz_m 00000100 .. 011 110 101 ... ..... ..... @rd_pg_rn FABS_m 00000100 .. 011 100 101 ... ..... ..... @rd_pg_rn FNEG_m 00000100 .. 011 101 101 ... ..... ..... @rd_pg_rn +CLS_z 00000100 .. 001 000 101 ... ..... ..... @rd_pg_rn +CLZ_z 00000100 .. 001 001 101 ... ..... ..... @rd_pg_rn +CNT_zpz_z 00000100 .. 001 010 101 ... ..... ..... @rd_pg_rn +CNOT_z 00000100 .. 001 011 101 ... ..... ..... @rd_pg_rn +NOT_zpz_z 00000100 .. 001 110 101 ... ..... ..... @rd_pg_rn +FABS_z 00000100 .. 001 100 101 ... ..... ..... @rd_pg_rn +FNEG_z 00000100 .. 001 101 101 ... ..... ..... @rd_pg_rn + # SVE integer unary operations (predicated) # Note esz > original size for extensions. ABS_m 00000100 .. 010 110 101 ... ..... ..... @rd_pg_rn diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3 * const sve_cls_fns[4] = { gen_helper_sve_cls_s, gen_helper_sve_cls_d, }; TRANS_FEAT(CLS_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sve_cls_fns[a->esz], a, 0) +TRANS_FEAT(CLS_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, sve_cls_fns[a->esz], a, 1) static gen_helper_gvec_3 * const sve_clz_fns[4] = { gen_helper_sve_clz_b, gen_helper_sve_clz_h, gen_helper_sve_clz_s, gen_helper_sve_clz_d, }; TRANS_FEAT(CLZ_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sve_clz_fns[a->esz], a, 0) +TRANS_FEAT(CLZ_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, sve_clz_fns[a->esz], a, 1) static gen_helper_gvec_3 * const sve_cnt_zpz_fns[4] = { gen_helper_sve_cnt_zpz_b, gen_helper_sve_cnt_zpz_h, gen_helper_sve_cnt_zpz_s, gen_helper_sve_cnt_zpz_d, }; -TRANS_FEAT(CNT_zpz_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sve_cnt_zpz_fns[a->esz], a, 0) +TRANS_FEAT(CNT_zpz_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, + sve_cnt_zpz_fns[a->esz], a, 0) +TRANS_FEAT(CNT_zpz_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + sve_cnt_zpz_fns[a->esz], a, 1) static gen_helper_gvec_3 * const sve_cnot_fns[4] = { gen_helper_sve_cnot_b, gen_helper_sve_cnot_h, gen_helper_sve_cnot_s, gen_helper_sve_cnot_d, }; -TRANS_FEAT(CNOT_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sve_cnot_fns[a->esz], a, 0) +TRANS_FEAT(CNOT_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, + sve_cnot_fns[a->esz], a, 0) +TRANS_FEAT(CNOT_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + sve_cnot_fns[a->esz], a, 1) static gen_helper_gvec_3 * const sve_not_zpz_fns[4] = { gen_helper_sve_not_zpz_b, gen_helper_sve_not_zpz_h, gen_helper_sve_not_zpz_s, gen_helper_sve_not_zpz_d, }; -TRANS_FEAT(NOT_zpz_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sve_not_zpz_fns[a->esz], a, 0) +TRANS_FEAT(NOT_zpz_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, + sve_not_zpz_fns[a->esz], a, 0) +TRANS_FEAT(NOT_zpz_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + sve_not_zpz_fns[a->esz], a, 1) static gen_helper_gvec_3 * const sve_abs_fns[4] = { gen_helper_sve_abs_b, gen_helper_sve_abs_h, @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3 * const fabs_ah_fns[4] = { }; TRANS_FEAT(FABS_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, s->fpcr_ah ? fabs_ah_fns[a->esz] : fabs_fns[a->esz], a, 0) +TRANS_FEAT(FABS_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + s->fpcr_ah ? fabs_ah_fns[a->esz] : fabs_fns[a->esz], a, 1) static gen_helper_gvec_3 * const fneg_fns[4] = { NULL, gen_helper_sve_fneg_h, @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3 * const fneg_ah_fns[4] = { }; TRANS_FEAT(FNEG_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, s->fpcr_ah ? fneg_ah_fns[a->esz] : fneg_fns[a->esz], a, 0) +TRANS_FEAT(FNEG_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + s->fpcr_ah ? fneg_ah_fns[a->esz] : fneg_fns[a->esz], a, 1) static gen_helper_gvec_3 * const sxtb_fns[4] = { NULL, gen_helper_sve_sxtb_h, -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> This includes REVB, REVH, REVW, RBIT. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/sve.decode | 5 +++++ target/arm/tcg/translate-sve.c | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -XXX,XX +XXX,XX @@ REVW_m 00000101 .. 1001 10 100 ... ..... ..... @rd_pg_rn RBIT_m 00000101 .. 1001 11 100 ... ..... ..... @rd_pg_rn REVD_m 00000101 00 1011 10 100 ... ..... ..... @rd_pg_rn_e0 +REVB_z 00000101 .. 1001 00 101 ... ..... ..... @rd_pg_rn +REVH_z 00000101 .. 1001 01 101 ... ..... ..... @rd_pg_rn +REVW_z 00000101 .. 1001 10 101 ... ..... ..... @rd_pg_rn +RBIT_z 00000101 .. 1001 11 101 ... ..... ..... @rd_pg_rn + # SVE vector splice (predicated, destructive) SPLICE 00000101 .. 101 100 100 ... ..... ..... @rdn_pg_rm diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3 * const sve_rbit_fns[4] = { gen_helper_sve_rbit_s, gen_helper_sve_rbit_d, }; TRANS_FEAT(RBIT_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, sve_rbit_fns[a->esz], a, 0) +TRANS_FEAT(RBIT_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, sve_rbit_fns[a->esz], a, 1) static gen_helper_gvec_3 * const sve2p1_orqv_fns[4] = { gen_helper_sve2p1_orqv_b, gen_helper_sve2p1_orqv_h, @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3 * const revb_fns[4] = { }; TRANS_FEAT(REVB_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, revb_fns[a->esz], a, 0) +TRANS_FEAT(REVB_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + revb_fns[a->esz], a, 1) static gen_helper_gvec_3 * const revh_fns[4] = { NULL, NULL, gen_helper_sve_revh_s, gen_helper_sve_revh_d, }; TRANS_FEAT(REVH_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, revh_fns[a->esz], a, 0) +TRANS_FEAT(REVH_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + revh_fns[a->esz], a, 1) TRANS_FEAT(REVW_m, aa64_sme_or_sve, gen_gvec_ool_arg_zpz, a->esz == 3 ? gen_helper_sve_revw_d : NULL, a, 0) +TRANS_FEAT(REVW_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + a->esz == 3 ? gen_helper_sve_revw_d : NULL, a, 1) TRANS_FEAT(REVD_m, aa64_sme_or_sve2p1, gen_gvec_ool_arg_zpz, gen_helper_sme_revd_q, a, 0) -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/sve.decode | 1 + target/arm/tcg/sve_helper.c | 4 ++++ target/arm/tcg/translate-sve.c | 2 ++ 3 files changed, 7 insertions(+) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -XXX,XX +XXX,XX @@ REVB_z 00000101 .. 1001 00 101 ... ..... ..... @rd_pg_rn REVH_z 00000101 .. 1001 01 101 ... ..... ..... @rd_pg_rn REVW_z 00000101 .. 1001 10 101 ... ..... ..... @rd_pg_rn RBIT_z 00000101 .. 1001 11 101 ... ..... ..... @rd_pg_rn +REVD_z 00000101 00 1011 10 101 ... ..... ..... @rd_pg_rn_e0 # SVE vector splice (predicated, destructive) SPLICE 00000101 .. 101 100 100 ... ..... ..... @rdn_pg_rm diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -XXX,XX +XXX,XX @@ DO_ZPZ_D(sve_revw_d, uint64_t, wswap64) void HELPER(sme_revd_q)(void *vd, void *vn, void *vg, uint32_t desc) { intptr_t i, opr_sz = simd_oprsz(desc) / 8; + bool zeroing = simd_data(desc) & 1; uint64_t *d = vd, *n = vn; uint8_t *pg = vg; @@ -XXX,XX +XXX,XX @@ void HELPER(sme_revd_q)(void *vd, void *vn, void *vg, uint32_t desc) uint64_t n1 = n[i + 1]; d[i + 0] = n1; d[i + 1] = n0; + } else if (zeroing) { + d[i + 0] = 0; + d[i + 1] = 0; } } } diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -XXX,XX +XXX,XX @@ TRANS_FEAT(REVW_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, TRANS_FEAT(REVD_m, aa64_sme_or_sve2p1, gen_gvec_ool_arg_zpz, gen_helper_sme_revd_q, a, 0) +TRANS_FEAT(REVD_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + gen_helper_sme_revd_q, a, 1) TRANS_FEAT(SPLICE, aa64_sme_or_sve, gen_gvec_ool_arg_zpzz, gen_helper_sve_splice, a, a->esz) -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> This includes URECPE, URSQRTE, SQABS, SQNEG. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/sve.decode | 5 +++++ target/arm/tcg/translate-sve.c | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -XXX,XX +XXX,XX @@ URSQRTE_m 01000100 .. 000 001 101 ... ..... ..... @rd_pg_rn SQABS_m 01000100 .. 001 000 101 ... ..... ..... @rd_pg_rn SQNEG_m 01000100 .. 001 001 101 ... ..... ..... @rd_pg_rn +URECPE_z 01000100 .. 000 010 101 ... ..... ..... @rd_pg_rn +URSQRTE_z 01000100 .. 000 011 101 ... ..... ..... @rd_pg_rn +SQABS_z 01000100 .. 001 010 101 ... ..... ..... @rd_pg_rn +SQNEG_z 01000100 .. 001 011 101 ... ..... ..... @rd_pg_rn + ### SVE2 saturating/rounding bitwise shift left (predicated) SRSHL 01000100 .. 000 010 100 ... ..... ..... @rdn_pg_rm diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -XXX,XX +XXX,XX @@ TRANS_FEAT(UADALP_zpzz, aa64_sme_or_sve2, gen_gvec_ool_arg_zpzz, TRANS_FEAT(URECPE_m, aa64_sme_or_sve2, gen_gvec_ool_arg_zpz, a->esz == 2 ? gen_helper_sve2_urecpe_s : NULL, a, 0) +TRANS_FEAT(URECPE_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + a->esz == 2 ? gen_helper_sve2_urecpe_s : NULL, a, 1) TRANS_FEAT(URSQRTE_m, aa64_sme_or_sve2, gen_gvec_ool_arg_zpz, a->esz == 2 ? gen_helper_sve2_ursqrte_s : NULL, a, 0) +TRANS_FEAT(URSQRTE_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, + a->esz == 2 ? gen_helper_sve2_ursqrte_s : NULL, a, 1) static gen_helper_gvec_3 * const sqabs_fns[4] = { gen_helper_sve2_sqabs_b, gen_helper_sve2_sqabs_h, gen_helper_sve2_sqabs_s, gen_helper_sve2_sqabs_d, }; TRANS_FEAT(SQABS_m, aa64_sme_or_sve2, gen_gvec_ool_arg_zpz, sqabs_fns[a->esz], a, 0) +TRANS_FEAT(SQABS_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, sqabs_fns[a->esz], a, 1) static gen_helper_gvec_3 * const sqneg_fns[4] = { gen_helper_sve2_sqneg_b, gen_helper_sve2_sqneg_h, gen_helper_sve2_sqneg_s, gen_helper_sve2_sqneg_d, }; TRANS_FEAT(SQNEG_m, aa64_sme_or_sve2, gen_gvec_ool_arg_zpz, sqneg_fns[a->esz], a, 0) +TRANS_FEAT(SQNEG_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, sqneg_fns[a->esz], a, 1) DO_ZPZZ(SQSHL, aa64_sme_or_sve2, sve2_sqshl) DO_ZPZZ(SQRSHL, aa64_sme_or_sve2, sve2_sqrshl) -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> Prepare for needing a non-zero value. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/translate-sve.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -XXX,XX +XXX,XX @@ TRANS_FEAT(FRINTX_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); static bool do_frint_mode(DisasContext *s, arg_rpr_esz *a, - ARMFPRounding mode, gen_helper_gvec_3_ptr *fn) + ARMFPRounding mode, int data, + gen_helper_gvec_3_ptr *fn) { unsigned vsz; TCGv_i32 tmode; @@ -XXX,XX +XXX,XX @@ static bool do_frint_mode(DisasContext *s, arg_rpr_esz *a, tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), vec_full_reg_offset(s, a->rn), pred_full_reg_offset(s, a->pg), - status, vsz, vsz, 0, fn); + status, vsz, vsz, data, fn); gen_restore_rmode(tmode, status); return true; } TRANS_FEAT(FRINTN_m, aa64_sme_or_sve, do_frint_mode, a, - FPROUNDING_TIEEVEN, frint_fns[a->esz]) + FPROUNDING_TIEEVEN, 0, frint_fns[a->esz]) TRANS_FEAT(FRINTP_m, aa64_sme_or_sve, do_frint_mode, a, - FPROUNDING_POSINF, frint_fns[a->esz]) + FPROUNDING_POSINF, 0, frint_fns[a->esz]) TRANS_FEAT(FRINTM_m, aa64_sme_or_sve, do_frint_mode, a, - FPROUNDING_NEGINF, frint_fns[a->esz]) + FPROUNDING_NEGINF, 0, frint_fns[a->esz]) TRANS_FEAT(FRINTZ_m, aa64_sme_or_sve, do_frint_mode, a, - FPROUNDING_ZERO, frint_fns[a->esz]) + FPROUNDING_ZERO, 0, frint_fns[a->esz]) TRANS_FEAT(FRINTA_m, aa64_sme_or_sve, do_frint_mode, a, - FPROUNDING_TIEAWAY, frint_fns[a->esz]) + FPROUNDING_TIEAWAY, 0, frint_fns[a->esz]) static gen_helper_gvec_3_ptr * const frecpx_fns[] = { NULL, gen_helper_sve_frecpx_h, @@ -XXX,XX +XXX,XX @@ TRANS_FEAT(FCVTLT_sd_m, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, gen_helper_sve2_fcvtlt_sd, a, 0, FPST_A64) TRANS_FEAT(FCVTX_ds_m, aa64_sme_or_sve2, do_frint_mode, a, - FPROUNDING_ODD, gen_helper_sve_fcvt_ds) + FPROUNDING_ODD, 0, gen_helper_sve_fcvt_ds) TRANS_FEAT(FCVTXNT_ds_m, aa64_sme_or_sve2, do_frint_mode, a, - FPROUNDING_ODD, gen_helper_sve2_fcvtnt_ds) + FPROUNDING_ODD, 0, gen_helper_sve2_fcvtnt_ds) static gen_helper_gvec_3_ptr * const flogb_fns[] = { NULL, gen_helper_flogb_h, -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> This is the various FRINT rounding modes. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/sve.decode | 8 ++++++++ target/arm/tcg/translate-sve.c | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -XXX,XX +XXX,XX @@ FRINTA_m 01100101 .. 000 100 101 ... ..... ..... @rd_pg_rn FRINTX_m 01100101 .. 000 110 101 ... ..... ..... @rd_pg_rn FRINTI_m 01100101 .. 000 111 101 ... ..... ..... @rd_pg_rn +FRINTN_z 01100100 .. 011 000 100 ... ..... ..... @rd_pg_rn +FRINTP_z 01100100 .. 011 000 101 ... ..... ..... @rd_pg_rn +FRINTM_z 01100100 .. 011 000 110 ... ..... ..... @rd_pg_rn +FRINTZ_z 01100100 .. 011 000 111 ... ..... ..... @rd_pg_rn +FRINTA_z 01100100 .. 011 001 100 ... ..... ..... @rd_pg_rn +FRINTX_z 01100100 .. 011 001 110 ... ..... ..... @rd_pg_rn +FRINTI_z 01100100 .. 011 001 111 ... ..... ..... @rd_pg_rn + # SVE floating-point unary operations FRECPX_m 01100101 .. 001 100 101 ... ..... ..... @rd_pg_rn FSQRT_m 01100101 .. 001 101 101 ... ..... ..... @rd_pg_rn diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3_ptr * const frint_fns[] = { TRANS_FEAT(FRINTI_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, frint_fns[a->esz], a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) +TRANS_FEAT(FRINTI_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + frint_fns[a->esz], a, 1, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) static gen_helper_gvec_3_ptr * const frintx_fns[] = { NULL, @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3_ptr * const frintx_fns[] = { TRANS_FEAT(FRINTX_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, frintx_fns[a->esz], a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); +TRANS_FEAT(FRINTX_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + frintx_fns[a->esz], a, 1, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); static bool do_frint_mode(DisasContext *s, arg_rpr_esz *a, ARMFPRounding mode, int data, @@ -XXX,XX +XXX,XX @@ TRANS_FEAT(FRINTZ_m, aa64_sme_or_sve, do_frint_mode, a, TRANS_FEAT(FRINTA_m, aa64_sme_or_sve, do_frint_mode, a, FPROUNDING_TIEAWAY, 0, frint_fns[a->esz]) +TRANS_FEAT(FRINTN_z, aa64_sme2p2_or_sve2p2, do_frint_mode, a, + FPROUNDING_TIEEVEN, 1, frint_fns[a->esz]) +TRANS_FEAT(FRINTP_z, aa64_sme2p2_or_sve2p2, do_frint_mode, a, + FPROUNDING_POSINF, 1, frint_fns[a->esz]) +TRANS_FEAT(FRINTM_z, aa64_sme2p2_or_sve2p2, do_frint_mode, a, + FPROUNDING_NEGINF, 1, frint_fns[a->esz]) +TRANS_FEAT(FRINTZ_z, aa64_sme2p2_or_sve2p2, do_frint_mode, a, + FPROUNDING_ZERO, 1, frint_fns[a->esz]) +TRANS_FEAT(FRINTA_z, aa64_sme2p2_or_sve2p2, do_frint_mode, a, + FPROUNDING_TIEAWAY, 1, frint_fns[a->esz]) + static gen_helper_gvec_3_ptr * const frecpx_fns[] = { NULL, gen_helper_sve_frecpx_h, gen_helper_sve_frecpx_s, gen_helper_sve_frecpx_d, -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> This is FCVTX, FCVT and BFCVT. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-13-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/sve.decode | 11 +++++++++++ target/arm/tcg/translate-sve.c | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -XXX,XX +XXX,XX @@ FCVT_hd_m 01100101 11 0010 01 101 ... ..... ..... @rd_pg_rn_e0 FCVT_ds_m 01100101 11 0010 10 101 ... ..... ..... @rd_pg_rn_e0 FCVT_sd_m 01100101 11 0010 11 101 ... ..... ..... @rd_pg_rn_e0 +FCVTX_ds_z 01100100 00 0110 101 10 ... ..... ..... @rd_pg_rn_e0 + +FCVT_sh_z 01100100 10 0110 101 00 ... ..... ..... @rd_pg_rn_e0 +FCVT_hs_z 01100100 10 0110 101 01 ... ..... ..... @rd_pg_rn_e0 +BFCVT_z 01100100 10 0110 101 10 ... ..... ..... @rd_pg_rn_e0 + +FCVT_dh_z 01100100 11 0110 101 00 ... ..... ..... @rd_pg_rn_e0 +FCVT_hd_z 01100100 11 0110 101 01 ... ..... ..... @rd_pg_rn_e0 +FCVT_ds_z 01100100 11 0110 101 10 ... ..... ..... @rd_pg_rn_e0 +FCVT_sd_z 01100100 11 0110 101 11 ... ..... ..... @rd_pg_rn_e0 + # SVE floating-point convert to integer FCVTZS_hh_m 01100101 01 011 01 0 101 ... ..... ..... @rd_pg_rn_e0 FCVTZU_hh_m 01100101 01 011 01 1 101 ... ..... ..... @rd_pg_rn_e0 diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -XXX,XX +XXX,XX @@ TRANS_FEAT(FCMLA_zzxz, aa64_sme_or_sve, gen_gvec_fpst_zzzz, fcmla_idx_fns[a->esz TRANS_FEAT(FCVT_sh_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_sh, a, 0, FPST_A64) +TRANS_FEAT(FCVT_sh_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvt_sh, a, 1, FPST_A64) + TRANS_FEAT(FCVT_hs_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_hs, a, 0, FPST_A64_F16) +TRANS_FEAT(FCVT_hs_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvt_hs, a, 1, FPST_A64_F16) TRANS_FEAT(BFCVT_m, aa64_sme_sve_bf16, gen_gvec_fpst_arg_zpz, gen_helper_sve_bfcvt, a, 0, s->fpcr_ah ? FPST_AH : FPST_A64) +TRANS_FEAT(BFCVT_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_bfcvt, a, 1, + s->fpcr_ah ? FPST_AH : FPST_A64) TRANS_FEAT(FCVT_dh_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_dh, a, 0, FPST_A64) +TRANS_FEAT(FCVT_dh_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvt_dh, a, 1, FPST_A64) + TRANS_FEAT(FCVT_hd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_hd, a, 0, FPST_A64_F16) +TRANS_FEAT(FCVT_hd_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvt_hd, a, 1, FPST_A64_F16) + TRANS_FEAT(FCVT_ds_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_ds, a, 0, FPST_A64) +TRANS_FEAT(FCVT_ds_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvt_ds, a, 1, FPST_A64) + TRANS_FEAT(FCVT_sd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_sd, a, 0, FPST_A64) +TRANS_FEAT(FCVT_sd_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvt_sd, a, 1, FPST_A64) TRANS_FEAT(FCVTZS_hh_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzs_hh, a, 0, FPST_A64_F16) @@ -XXX,XX +XXX,XX @@ TRANS_FEAT(FCVTLT_sd_m, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, TRANS_FEAT(FCVTX_ds_m, aa64_sme_or_sve2, do_frint_mode, a, FPROUNDING_ODD, 0, gen_helper_sve_fcvt_ds) +TRANS_FEAT(FCVTX_ds_z, aa64_sme2p2_or_sve2p2, do_frint_mode, a, + FPROUNDING_ODD, 1, gen_helper_sve_fcvt_ds) + TRANS_FEAT(FCVTXNT_ds_m, aa64_sme_or_sve2, do_frint_mode, a, FPROUNDING_ODD, 0, gen_helper_sve2_fcvtnt_ds) -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> This is FRECPX and FSQRT. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-14-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/sve.decode | 3 +++ target/arm/tcg/translate-sve.c | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -XXX,XX +XXX,XX @@ FRINTI_z 01100100 .. 011 001 111 ... ..... ..... @rd_pg_rn FRECPX_m 01100101 .. 001 100 101 ... ..... ..... @rd_pg_rn FSQRT_m 01100101 .. 001 101 101 ... ..... ..... @rd_pg_rn +FRECPX_z 01100100 .. 011 011 100 ... ..... ..... @rd_pg_rn +FSQRT_z 01100100 .. 011 011 101 ... ..... ..... @rd_pg_rn + # SVE integer convert to floating-point SCVTF_hh_m 01100101 01 010 01 0 101 ... ..... ..... @rd_pg_rn_e0 SCVTF_sh_m 01100101 01 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3_ptr * const frecpx_fns[] = { }; TRANS_FEAT(FRECPX_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, frecpx_fns[a->esz], a, 0, select_ah_fpst(s, a->esz)) +TRANS_FEAT(FRECPX_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + frecpx_fns[a->esz], a, 1, select_ah_fpst(s, a->esz)) static gen_helper_gvec_3_ptr * const fsqrt_fns[] = { NULL, gen_helper_sve_fsqrt_h, @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3_ptr * const fsqrt_fns[] = { TRANS_FEAT(FSQRT_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, fsqrt_fns[a->esz], a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) +TRANS_FEAT(FSQRT_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + fsqrt_fns[a->esz], a, 1, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) TRANS_FEAT(SCVTF_hh_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_scvt_hh, a, 0, FPST_A64_F16) -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-15-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/sve.decode | 16 ++++++++++++++++ target/arm/tcg/translate-sve.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -XXX,XX +XXX,XX @@ UCVTF_sd_m 01100101 11 010 00 1 101 ... ..... ..... @rd_pg_rn_e0 UCVTF_ds_m 01100101 11 010 10 1 101 ... ..... ..... @rd_pg_rn_e0 UCVTF_dd_m 01100101 11 010 11 1 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_hh_z 01100100 01 011 10 0 110 ... ..... ..... @rd_pg_rn_e0 +SCVTF_sh_z 01100100 01 011 10 1 100 ... ..... ..... @rd_pg_rn_e0 +SCVTF_ss_z 01100100 10 011 10 1 100 ... ..... ..... @rd_pg_rn_e0 +SCVTF_sd_z 01100100 11 011 10 0 100 ... ..... ..... @rd_pg_rn_e0 +SCVTF_dh_z 01100100 01 011 10 1 110 ... ..... ..... @rd_pg_rn_e0 +SCVTF_ds_z 01100100 11 011 10 1 100 ... ..... ..... @rd_pg_rn_e0 +SCVTF_dd_z 01100100 11 011 10 1 110 ... ..... ..... @rd_pg_rn_e0 + +UCVTF_hh_z 01100100 01 011 10 0 111 ... ..... ..... @rd_pg_rn_e0 +UCVTF_sh_z 01100100 01 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_ss_z 01100100 10 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_sd_z 01100100 11 011 10 0 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_dh_z 01100100 01 011 10 1 111 ... ..... ..... @rd_pg_rn_e0 +UCVTF_ds_z 01100100 11 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_dd_z 01100100 11 011 10 1 111 ... ..... ..... @rd_pg_rn_e0 + ### SVE Memory - 32-bit Gather and Unsized Contiguous Group # SVE load predicate register diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -XXX,XX +XXX,XX @@ TRANS_FEAT(UCVTF_sd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, TRANS_FEAT(UCVTF_dd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_ucvt_dd, a, 0, FPST_A64) +TRANS_FEAT(SCVTF_hh_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_scvt_hh, a, 1, FPST_A64_F16) +TRANS_FEAT(SCVTF_sh_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_scvt_sh, a, 1, FPST_A64_F16) +TRANS_FEAT(SCVTF_dh_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_scvt_dh, a, 1, FPST_A64_F16) + +TRANS_FEAT(SCVTF_ss_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_scvt_ss, a, 1, FPST_A64) +TRANS_FEAT(SCVTF_ds_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_scvt_ds, a, 1, FPST_A64) + +TRANS_FEAT(SCVTF_sd_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_scvt_sd, a, 1, FPST_A64) +TRANS_FEAT(SCVTF_dd_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_scvt_dd, a, 1, FPST_A64) + +TRANS_FEAT(UCVTF_hh_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_ucvt_hh, a, 1, FPST_A64_F16) +TRANS_FEAT(UCVTF_sh_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_ucvt_sh, a, 1, FPST_A64_F16) +TRANS_FEAT(UCVTF_dh_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_ucvt_dh, a, 1, FPST_A64_F16) + +TRANS_FEAT(UCVTF_ss_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_ucvt_ss, a, 1, FPST_A64) +TRANS_FEAT(UCVTF_ds_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_ucvt_ds, a, 1, FPST_A64) +TRANS_FEAT(UCVTF_sd_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_ucvt_sd, a, 1, FPST_A64) + +TRANS_FEAT(UCVTF_dd_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_ucvt_dd, a, 1, FPST_A64) + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-16-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/helper-sve-defs.h | 9 ++++++++ target/arm/tcg/sve.decode | 20 ++++++++++++++++++ target/arm/tcg/sve_helper.c | 5 +++++ target/arm/tcg/translate-sve.c | 36 ++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+) diff --git a/target/arm/tcg/helper-sve-defs.h b/target/arm/tcg/helper-sve-defs.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/helper-sve-defs.h +++ b/target/arm/tcg/helper-sve-defs.h @@ -XXX,XX +XXX,XX @@ DEF_HELPER_FLAGS_5(sve_frintx_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve_frintx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p2_frint32_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p2_frint64_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p2_frint32_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p2_frint64_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + DEF_HELPER_FLAGS_5(sve_frecpx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frecpx_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -XXX,XX +XXX,XX @@ FRINTA_z 01100100 .. 011 001 100 ... ..... ..... @rd_pg_rn FRINTX_z 01100100 .. 011 001 110 ... ..... ..... @rd_pg_rn FRINTI_z 01100100 .. 011 001 111 ... ..... ..... @rd_pg_rn +FRINT32X_s_m 01100101 00 010 001 101 ... ..... ..... @rd_pg_rn_e0 +FRINT32X_d_m 01100101 00 010 011 101 ... ..... ..... @rd_pg_rn_e0 +FRINT64X_s_m 01100101 00 010 101 101 ... ..... ..... @rd_pg_rn_e0 +FRINT64X_d_m 01100101 00 010 111 101 ... ..... ..... @rd_pg_rn_e0 + +FRINT32X_s_z 01100100 00 011 100 101 ... ..... ..... @rd_pg_rn_e0 +FRINT32X_d_z 01100100 00 011 100 111 ... ..... ..... @rd_pg_rn_e0 +FRINT64X_s_z 01100100 00 011 101 101 ... ..... ..... @rd_pg_rn_e0 +FRINT64X_d_z 01100100 00 011 101 111 ... ..... ..... @rd_pg_rn_e0 + +FRINT32Z_s_m 01100101 00 010 000 101 ... ..... ..... @rd_pg_rn_e0 +FRINT32Z_d_m 01100101 00 010 010 101 ... ..... ..... @rd_pg_rn_e0 +FRINT64Z_s_m 01100101 00 010 100 101 ... ..... ..... @rd_pg_rn_e0 +FRINT64Z_d_m 01100101 00 010 110 101 ... ..... ..... @rd_pg_rn_e0 + +FRINT32Z_s_z 01100100 00 011 100 100 ... ..... ..... @rd_pg_rn_e0 +FRINT32Z_d_z 01100100 00 011 100 110 ... ..... ..... @rd_pg_rn_e0 +FRINT64Z_s_z 01100100 00 011 101 100 ... ..... ..... @rd_pg_rn_e0 +FRINT64Z_d_z 01100100 00 011 101 110 ... ..... ..... @rd_pg_rn_e0 + # SVE floating-point unary operations FRECPX_m 01100101 .. 001 100 101 ... ..... ..... @rd_pg_rn FSQRT_m 01100101 .. 001 101 101 ... ..... ..... @rd_pg_rn diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -XXX,XX +XXX,XX @@ DO_ZPZ_FP(sve_frintx_h, uint16_t, H1_2, float16_round_to_int) DO_ZPZ_FP(sve_frintx_s, uint32_t, H1_4, float32_round_to_int) DO_ZPZ_FP(sve_frintx_d, uint64_t, H1_8, float64_round_to_int) +DO_ZPZ_FP(sve2p2_frint32_s, uint32_t, H1_4, helper_frint32_s) +DO_ZPZ_FP(sve2p2_frint64_s, uint32_t, H1_4, helper_frint64_s) +DO_ZPZ_FP(sve2p2_frint32_d, uint64_t, H1_8, helper_frint32_d) +DO_ZPZ_FP(sve2p2_frint64_d, uint64_t, H1_8, helper_frint64_d) + DO_ZPZ_FP(sve_frecpx_h, uint16_t, H1_2, helper_frecpx_f16) DO_ZPZ_FP(sve_frecpx_s, uint32_t, H1_4, helper_frecpx_f32) DO_ZPZ_FP(sve_frecpx_d, uint64_t, H1_8, helper_frecpx_f64) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -XXX,XX +XXX,XX @@ TRANS_FEAT(FRINTZ_z, aa64_sme2p2_or_sve2p2, do_frint_mode, a, TRANS_FEAT(FRINTA_z, aa64_sme2p2_or_sve2p2, do_frint_mode, a, FPROUNDING_TIEAWAY, 1, frint_fns[a->esz]) +TRANS_FEAT(FRINT32X_s_m, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve2p2_frint32_s, a, 0, FPST_A64) +TRANS_FEAT(FRINT32X_d_m, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve2p2_frint32_d, a, 0, FPST_A64) +TRANS_FEAT(FRINT64X_s_m, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve2p2_frint64_s, a, 0, FPST_A64) +TRANS_FEAT(FRINT64X_d_m, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve2p2_frint64_d, a, 0, FPST_A64) + +TRANS_FEAT(FRINT32X_s_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve2p2_frint32_s, a, 1, FPST_A64) +TRANS_FEAT(FRINT32X_d_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve2p2_frint32_d, a, 1, FPST_A64) +TRANS_FEAT(FRINT64X_s_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve2p2_frint64_s, a, 1, FPST_A64) +TRANS_FEAT(FRINT64X_d_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve2p2_frint64_d, a, 1, FPST_A64) + +TRANS_FEAT(FRINT32Z_s_m, aa64_sme2p2_or_sve2p2, do_frint_mode, + a, FPROUNDING_ZERO, 0, gen_helper_sve2p2_frint32_s) +TRANS_FEAT(FRINT32Z_d_m, aa64_sme2p2_or_sve2p2, do_frint_mode, + a, FPROUNDING_ZERO, 0, gen_helper_sve2p2_frint32_d) +TRANS_FEAT(FRINT64Z_s_m, aa64_sme2p2_or_sve2p2, do_frint_mode, + a, FPROUNDING_ZERO, 0, gen_helper_sve2p2_frint64_s) +TRANS_FEAT(FRINT64Z_d_m, aa64_sme2p2_or_sve2p2, do_frint_mode, + a, FPROUNDING_ZERO, 0, gen_helper_sve2p2_frint64_d) + +TRANS_FEAT(FRINT32Z_s_z, aa64_sme2p2_or_sve2p2, do_frint_mode, + a, FPROUNDING_ZERO, 1, gen_helper_sve2p2_frint32_s) +TRANS_FEAT(FRINT32Z_d_z, aa64_sme2p2_or_sve2p2, do_frint_mode, + a, FPROUNDING_ZERO, 1, gen_helper_sve2p2_frint32_d) +TRANS_FEAT(FRINT64Z_s_z, aa64_sme2p2_or_sve2p2, do_frint_mode, + a, FPROUNDING_ZERO, 1, gen_helper_sve2p2_frint64_s) +TRANS_FEAT(FRINT64Z_d_z, aa64_sme2p2_or_sve2p2, do_frint_mode, + a, FPROUNDING_ZERO, 1, gen_helper_sve2p2_frint64_d) + static gen_helper_gvec_3_ptr * const frecpx_fns[] = { NULL, gen_helper_sve_frecpx_h, gen_helper_sve_frecpx_s, gen_helper_sve_frecpx_d, -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> Use the low bit of simd_data to hold a 'zeroing' bit. The simd_data field is currently unused and always 0. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-17-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/sve_helper.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -XXX,XX +XXX,XX @@ void HELPER(NAME)(void *vd, void *vn, void *vg, \ float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc); \ + bool zeroing = simd_data(desc) & 1; \ uint64_t *g = vg; \ do { \ uint64_t pg = g[(i - 1) >> 6]; \ @@ -XXX,XX +XXX,XX @@ void HELPER(NAME)(void *vd, void *vn, void *vg, \ if (likely((pg >> (i & 63)) & 1)) { \ TYPEW nn = *(TYPEW *)(vn + HW(i)); \ *(TYPEN *)(vd + HN(i + sizeof(TYPEN))) = OP(nn, status); \ + } else if (zeroing) { \ + *(TYPEN *)(vd + HN(i + sizeof(TYPEN))) = 0; \ } \ } while (i & 63); \ } while (i != 0); \ @@ -XXX,XX +XXX,XX @@ void HELPER(NAME)(void *vd, void *vn, void *vg, \ float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc); \ + bool zeroing = simd_data(desc) & 1; \ uint64_t *g = vg; \ do { \ uint64_t pg = g[(i - 1) >> 6]; \ @@ -XXX,XX +XXX,XX @@ void HELPER(NAME)(void *vd, void *vn, void *vg, \ if (likely((pg >> (i & 63)) & 1)) { \ TYPEN nn = *(TYPEN *)(vn + HN(i + sizeof(TYPEN))); \ *(TYPEW *)(vd + HW(i)) = OP(nn, status); \ + } else if (zeroing) { \ + *(TYPEW *)(vd + HW(i)) = 0; \ } \ } while (i & 63); \ } while (i != 0); \ -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> This includes FCVTXNT, BFCVTNT, FCVTNT, FCVTLT. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-18-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/sve.decode | 7 +++++++ target/arm/tcg/translate-sve.c | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -XXX,XX +XXX,XX @@ FCVTLT_hs_m 01100100 10 0010 01 101 ... ..... ..... @rd_pg_rn_e0 FCVTNT_ds_m 01100100 11 0010 10 101 ... ..... ..... @rd_pg_rn_e0 FCVTLT_sd_m 01100100 11 0010 11 101 ... ..... ..... @rd_pg_rn_e0 +FCVTXNT_ds_z 01100100 00 0000 10 101 ... ..... ..... @rd_pg_rn_e0 +FCVTNT_sh_z 01100100 10 0000 00 101 ... ..... ..... @rd_pg_rn_e0 +FCVTNT_ds_z 01100100 11 0000 10 101 ... ..... ..... @rd_pg_rn_e0 +BFCVTNT_z 01100100 10 0000 10 101 ... ..... ..... @rd_pg_rn_e0 +FCVTLT_hs_z 01100100 10 0000 01 101 ... ..... ..... @rd_pg_rn_e0 +FCVTLT_sd_z 01100100 11 0000 11 101 ... ..... ..... @rd_pg_rn_e0 + ### SVE2 floating-point convert to integer FLOGB_m 01100101 00 011 esz:2 0101 pg:3 rn:5 rd:5 &rpr_esz diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -XXX,XX +XXX,XX @@ static bool trans_RAX1(DisasContext *s, arg_RAX1 *a) TRANS_FEAT(FCVTNT_sh_m, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, gen_helper_sve2_fcvtnt_sh, a, 0, FPST_A64) +TRANS_FEAT(FCVTNT_sh_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve2_fcvtnt_sh, a, 1, FPST_A64) TRANS_FEAT(FCVTNT_ds_m, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, gen_helper_sve2_fcvtnt_ds, a, 0, FPST_A64) +TRANS_FEAT(FCVTNT_ds_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve2_fcvtnt_ds, a, 1, FPST_A64) TRANS_FEAT(BFCVTNT_m, aa64_sme_sve_bf16, gen_gvec_fpst_arg_zpz, gen_helper_sve_bfcvtnt, a, 0, s->fpcr_ah ? FPST_AH : FPST_A64) +TRANS_FEAT(BFCVTNT_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_bfcvtnt, a, 1, + s->fpcr_ah ? FPST_AH : FPST_A64) TRANS_FEAT(FCVTLT_hs_m, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, gen_helper_sve2_fcvtlt_hs, a, 0, FPST_A64_F16) +TRANS_FEAT(FCVTLT_hs_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve2_fcvtlt_hs, a, 1, FPST_A64_F16) TRANS_FEAT(FCVTLT_sd_m, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, gen_helper_sve2_fcvtlt_sd, a, 0, FPST_A64) +TRANS_FEAT(FCVTLT_sd_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve2_fcvtlt_sd, a, 1, FPST_A64) TRANS_FEAT(FCVTX_ds_m, aa64_sme_or_sve2, do_frint_mode, a, FPROUNDING_ODD, 0, gen_helper_sve_fcvt_ds) @@ -XXX,XX +XXX,XX @@ TRANS_FEAT(FCVTX_ds_z, aa64_sme2p2_or_sve2p2, do_frint_mode, a, TRANS_FEAT(FCVTXNT_ds_m, aa64_sme_or_sve2, do_frint_mode, a, FPROUNDING_ODD, 0, gen_helper_sve2_fcvtnt_ds) +TRANS_FEAT(FCVTXNT_ds_z, aa64_sme2p2_or_sve2p2, do_frint_mode, a, + FPROUNDING_ODD, 1, gen_helper_sve2_fcvtnt_ds) static gen_helper_gvec_3_ptr * const flogb_fns[] = { NULL, gen_helper_flogb_h, -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> This is FLOGB, FCVTZS, FCVTZU. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260604234852.573178-19-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/sve.decode | 16 +++++++++++++++ target/arm/tcg/translate-sve.c | 37 ++++++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -XXX,XX +XXX,XX @@ FCVTZU_sd_m 01100101 11 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 FCVTZS_dd_m 01100101 11 011 11 0 101 ... ..... ..... @rd_pg_rn_e0 FCVTZU_dd_m 01100101 11 011 11 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_hh_z 01100100 01 011 11 0 110 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_hh_z 01100100 01 011 11 0 111 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_hs_z 01100100 01 011 11 1 100 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_hs_z 01100100 01 011 11 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_hd_z 01100100 01 011 11 1 110 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_hd_z 01100100 01 011 11 1 111 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_ss_z 01100100 10 011 11 1 100 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_ss_z 01100100 10 011 11 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_sd_z 01100100 11 011 11 1 100 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_sd_z 01100100 11 011 11 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_ds_z 01100100 11 011 11 0 100 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_ds_z 01100100 11 011 11 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_dd_z 01100100 11 011 11 1 110 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_dd_z 01100100 11 011 11 1 111 ... ..... ..... @rd_pg_rn_e0 + # SVE floating-point round to integral value FRINTN_m 01100101 .. 000 000 101 ... ..... ..... @rd_pg_rn FRINTP_m 01100101 .. 000 001 101 ... ..... ..... @rd_pg_rn @@ -XXX,XX +XXX,XX @@ FCVTLT_sd_z 01100100 11 0000 11 101 ... ..... ..... @rd_pg_rn_e0 ### SVE2 floating-point convert to integer FLOGB_m 01100101 00 011 esz:2 0101 pg:3 rn:5 rd:5 &rpr_esz +FLOGB_z 01100100 00 011 1101 esz:2 pg:3 rn:5 rd:5 &rpr_esz ### SVE2 floating-point multiply-add long (vectors) FMLALB_zzzw 01100100 10 1 ..... 10 0 00 0 ..... ..... @rda_rn_rm_ex esz=2 diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -XXX,XX +XXX,XX @@ TRANS_FEAT(FCVTZS_dd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, TRANS_FEAT(FCVTZU_dd_m, aa64_sme_or_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzu_dd, a, 0, FPST_A64) +TRANS_FEAT(FCVTZS_hh_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvtzs_hh, a, 1, FPST_A64_F16) +TRANS_FEAT(FCVTZU_hh_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvtzu_hh, a, 1, FPST_A64_F16) +TRANS_FEAT(FCVTZS_hs_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvtzs_hs, a, 1, FPST_A64_F16) +TRANS_FEAT(FCVTZU_hs_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvtzu_hs, a, 1, FPST_A64_F16) +TRANS_FEAT(FCVTZS_hd_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvtzs_hd, a, 1, FPST_A64_F16) +TRANS_FEAT(FCVTZU_hd_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvtzu_hd, a, 1, FPST_A64_F16) + +TRANS_FEAT(FCVTZS_ss_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvtzs_ss, a, 1, FPST_A64) +TRANS_FEAT(FCVTZU_ss_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvtzu_ss, a, 1, FPST_A64) +TRANS_FEAT(FCVTZS_sd_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvtzs_sd, a, 1, FPST_A64) +TRANS_FEAT(FCVTZU_sd_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvtzu_sd, a, 1, FPST_A64) +TRANS_FEAT(FCVTZS_ds_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvtzs_ds, a, 1, FPST_A64) +TRANS_FEAT(FCVTZU_ds_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvtzu_ds, a, 1, FPST_A64) + +TRANS_FEAT(FCVTZS_dd_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvtzs_dd, a, 1, FPST_A64) +TRANS_FEAT(FCVTZU_dd_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + gen_helper_sve_fcvtzu_dd, a, 1, FPST_A64) + static gen_helper_gvec_3_ptr * const frint_fns[] = { NULL, gen_helper_sve_frint_h, @@ -XXX,XX +XXX,XX @@ static gen_helper_gvec_3_ptr * const flogb_fns[] = { NULL, gen_helper_flogb_h, gen_helper_flogb_s, gen_helper_flogb_d }; -TRANS_FEAT(FLOGB_m, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, flogb_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) +TRANS_FEAT(FLOGB_m, aa64_sme_or_sve2, gen_gvec_fpst_arg_zpz, + flogb_fns[a->esz], a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) +TRANS_FEAT(FLOGB_z, aa64_sme2p2_or_sve2p2, gen_gvec_fpst_arg_zpz, + flogb_fns[a->esz], a, 1, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) static bool do_FMLAL_zzzw(DisasContext *s, arg_rrrr_esz *a, bool sub, bool sel) { -- 2.43.0
Hi; here's another arm pullreq; main parts here are the new i.MX 8MM EVK board, and a lot of refactoring. thanks -- PMM The following changes since commit aa15257174da180c6a8a9d58f87319cfe61c5520: Merge tag 'pbouvier/pr/plugins-20260424' of https://gitlab.com/p-b-o/qemu into staging (2026-04-25 10:22:04 -0400) are available in the Git repository at: https://gitlab.com/pm215/qemu.git tags/pull-target-arm-20260427 for you to fetch changes up to 4575da5ecb7a27544aa6da1f78e700e8100ceaea: target/arm: report register in WFIT syndromes (2026-04-27 11:46:34 +0100) ---------------------------------------------------------------- target-arm queue: docs/system: add FEAT_AA32 and FEAT_AA64 to emulation list hw/arm: Add the i.MX 8MM EVK(Evaluation Kit) board target/arm: Build M-profile helper code once only hw/arm: Remove hw_error() for the unimplemented CM_LMBUSCNT register hw: Move ARM_SYSCTL_GPIO definitions to arm sysctl specific header target/arm: Allow 'aarch64=off' to be set for TCG CPUs target/arm: Allow some sysregs to not have to be an exact match for migration hw/arm/raspi4b: NOP all DTB nodes when removing unimplemented devices hw/arm/fsl-imx6ul: Implement LCDIF display device target/arm: Refactor syndrome value code to use registerfields target/arm: Report the register in WFxT syndromes ---------------------------------------------------------------- Alex Bennée (24): docs/system: add FEAT_AA32 and FEAT_AA64 to emulation list target/arm: migrate basic syndrome helpers to registerfields target/arm: migrate system/cp trap syndromes to registerfields target/arm: migrate FP/SIMD trap syndromes to registerfields target/arm: migrate eret trap syndromes to registerfields target/arm: migrate SME trap syndromes to registerfields target/arm: migrate PAC trap syndromes to registerfields target/arm: migrate BTI trap syndromes to registerfields target/arm: migrate BXJ trap syndromes to registerfields target/arm: migrate Granule Protection traps to registerfields target/arm: migrate fault syndromes to registerfields target/arm: migrate debug syndromes to registerfields target/arm: migrate wfx syndromes to registerfields target/arm: migrate gcs syndromes to registerfields target/arm: migrate memory op syndromes to registerfields target/arm: migrate check_hcr_el2_trap to use syndrome helper target/arm: use syndrome helpers in arm_cpu_do_interrupt_aarch32_hyp target/arm: use syndrome helpers to set SAME_EL EC bit target/arm: make whpx use syndrome helpers for decode target/arm: make hvf use syndrome helpers for decode target/arm: use syndrome helpers in merge_syn_data_abort target/arm: use syndrome helpers to query VNCR bit target/arm: remove old syndrome defines target/arm: report register in WFIT syndromes Eric Auger (7): target/arm/cpu: Introduce the infrastructure for cpreg migration tolerances target/arm/machine: Handle ToleranceNotOnBothEnds migration tolerances target/arm/machine: Handle ToleranceOnlySrcTestValue migration tolerance target/arm/cpu64: Mitigate migration failures due to spurious TCR_EL1, PIRE0_EL1 and PIR_EL1 target/arm/cpu64: Define cpreg migration tolerance for KVM_REG_ARM_VENDOR_HYP_BMAP_2 target/arm/helper: Define cpreg migration tolerance for DGBDTR_EL0 Revert "target/arm: Reinstate bogus AArch32 DBGDTRTX register for migration compat" Gaurav Sharma (15): hw/arm: Add the i.MX 8MM EVK(Evaluation Kit) board hw/misc/imx8mp_analog: Add property to analog device hw/arm/fsl-imx8mm: Add Analog device IP to iMX8MM SOC hw/arm/fsl-imx8mm: Add Clock Control Module IP to iMX8MM hw/arm/fsl-imx8mm: Implemented support for SNVS hw/arm/fsl-imx8mm: Adding support for USDHC storage controllers hw/arm/fsl-imx8mm: Add PCIe support hw/arm/fsl-imx8mm: Add GPIO controllers hw/arm/fsl-imx8mm: Adding support for I2C emulation hw/arm/fsl-imx8mm: Adding support for SPI controller hw/arm/fsl-imx8mm: Adding support for Watchdog Timers hw/arm/fsl-imx8mm: Adding support for General Purpose Timers hw/arm/fsl-imx8mm: Adding support for ENET ethernet controller hw/arm/fsl-imx8mm: Adding support for USB controller hw/arm/fsl-imx8mm: Adding functional testing of iMX8MM emulation Osama Abdelkader (1): hw/arm/raspi4b: NOP all DTB nodes when removing unimplemented devices Peter Maydell (4): hw: Move ARM_SYSCTL_GPIO definitions to arm sysctl specific header target/arm: Clear AArch64 ID regs from ARMISARegisters if AArch64 disabled target/arm: Allow 'aarch64=off' to be set for TCG CPUs tests/functional/aarch64: Add basic test of TCG aarch64=off Philippe Mathieu-Daudé (9): target/arm: Ignore endianness when setting MTE tags target/arm: Explode MO_TExx -> MO_TE | MO_xx target/arm: Hoist MO_TE into mve_advance_vpt() target/arm: Hoist MO_TE into MVE DO_VSTR() macro target/arm: Introduce mo_endian() helper target/arm: Replace MO_TE -> mo_endian() for MVE helpers target/arm: Compile mve_helper.c once target/arm: Replace MO_TE -> mo_endian() for Cortex-M helpers target/arm: Compile m_helper.c once Thomas Huth (1): hw/arm: Remove hw_error() for the unimplemented CM_LMBUSCNT register Yucai Liu (2): hw/display: Add i.MX6UL LCDIF device model hw/arm/fsl-imx6ul: Wire in the LCDIF device model MAINTAINERS | 16 +- docs/system/arm/cpu-features.rst | 10 +- docs/system/arm/emulation.rst | 2 + docs/system/arm/{imx8mp-evk.rst => imx8m.rst} | 49 +- docs/system/target-arm.rst | 2 +- hw/arm/Kconfig | 25 + hw/arm/fsl-imx6ul.c | 12 +- hw/arm/fsl-imx8mm.c | 704 ++++++++++++++++++++++ hw/arm/imx8mm-evk.c | 130 ++++ hw/arm/integratorcp.c | 3 - hw/arm/meson.build | 2 + hw/arm/raspi4b.c | 10 +- hw/arm/realview.c | 2 +- hw/arm/vexpress.c | 2 +- hw/arm/virt.c | 1 - hw/display/Kconfig | 4 + hw/display/imx6ul_lcdif.c | 453 ++++++++++++++ hw/display/meson.build | 1 + hw/misc/arm_sysctl.c | 2 +- hw/misc/imx8mp_analog.c | 12 +- hw/timer/imx_gpt.c | 26 + hw/vmapple/vmapple.c | 1 - include/hw/arm/fsl-imx6ul.h | 4 +- include/hw/arm/fsl-imx8mm.h | 242 ++++++++ include/hw/arm/primecell.h | 12 - include/hw/display/imx6ul_lcdif.h | 37 ++ include/hw/misc/arm_sysctl.h | 16 + include/hw/misc/imx8mp_analog.h | 3 + include/hw/timer/imx_gpt.h | 2 + target/arm/cpu-features.h | 5 + target/arm/cpu.c | 153 ++++- target/arm/cpu.h | 4 +- target/arm/cpu64.c | 39 ++ target/arm/debug_helper.c | 29 - target/arm/helper.c | 27 +- target/arm/hvf/hvf.c | 14 +- target/arm/internals.h | 60 ++ target/arm/machine.c | 33 +- target/arm/syndrome.h | 595 ++++++++++++++---- target/arm/tcg/debug.c | 2 +- target/arm/tcg/helper-a64.c | 2 +- target/arm/tcg/helper-defs.h | 2 +- target/arm/tcg/m_helper.c | 8 +- target/arm/tcg/meson.build | 6 +- target/arm/tcg/mve_helper.c | 102 ++-- target/arm/tcg/op_helper.c | 7 +- target/arm/tcg/tlb_helper.c | 6 +- target/arm/tcg/translate-a64.c | 2 +- target/arm/tcg/vfp_helper.c | 5 +- target/arm/trace-events | 2 + target/arm/whpx/whpx-all.c | 13 +- tests/functional/aarch64/meson.build | 3 + tests/functional/aarch64/test_imx8mm_evk.py | 69 +++ tests/functional/aarch64/test_virt_aarch64_off.py | 37 ++ tests/qtest/arm-cpu-features.c | 8 +- 55 files changed, 2743 insertions(+), 275 deletions(-) rename docs/system/arm/{imx8mp-evk.rst => imx8m.rst} (58%) create mode 100644 hw/arm/fsl-imx8mm.c create mode 100644 hw/arm/imx8mm-evk.c create mode 100644 hw/display/imx6ul_lcdif.c create mode 100644 include/hw/arm/fsl-imx8mm.h delete mode 100644 include/hw/arm/primecell.h create mode 100644 include/hw/display/imx6ul_lcdif.h create mode 100644 include/hw/misc/arm_sysctl.h create mode 100755 tests/functional/aarch64/test_imx8mm_evk.py create mode 100755 tests/functional/aarch64/test_virt_aarch64_off.py
From: Alex Bennée <alex.bennee@linaro.org> This is just a documentation tweak as we already support both. FEAT_AA32 implies FEAT_AA32EL0. FEAT_AA64 implies FEAT_AA64EL[0123]. This is however useful if you are using emulation.rst as a source of truth of what QEMU emulates and when cross checking with Features.json from Arm. Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260421093506.616307-1-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- docs/system/arm/emulation.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -XXX,XX +XXX,XX @@ QEMU's TCG emulation includes support for the Armv5, Armv6, Armv7, Armv8 and Armv9 versions of the A-profile architecture. It also has support for the following architecture extensions: +- FEAT_AA32 (PE Support for AArch32) - FEAT_AA32BF16 (AArch32 BFloat16 instructions) - FEAT_AA32EL0 (Support for AArch32 at EL0) - FEAT_AA32EL1 (Support for AArch32 at EL1) @@ -XXX,XX +XXX,XX @@ the following architecture extensions: - FEAT_AA32EL3 (Support for AArch32 at EL3) - FEAT_AA32HPD (AArch32 hierarchical permission disables) - FEAT_AA32I8MM (AArch32 Int8 matrix multiplication instructions) +- FEAT_AA64 (PE uses AArch64 after last reboot) - FEAT_AA64EL0 (Support for AArch64 at EL0) - FEAT_AA64EL1 (Support for AArch64 at EL1) - FEAT_AA64EL2 (Support for AArch64 at EL2) -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> Implemented CPUs, RAM, UARTs and Interrupt Controller Other peripherals are represented as TYPE_UNIMPLEMENTED_DEVICE Complete memory map of the SoC is provided. Set default RAM size to 2GB and default CPU count to 4 to match the real i.MX8MM EVK hardware configuration. Documentation is shared with imx8mp-evk to avoid duplication. Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> [PMM: fixed over-long lines in doc] Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- MAINTAINERS | 11 +- docs/system/arm/{imx8mp-evk.rst => imx8m.rst} | 49 ++- docs/system/target-arm.rst | 2 +- hw/arm/Kconfig | 12 + hw/arm/fsl-imx8mm.c | 377 ++++++++++++++++++ hw/arm/imx8mm-evk.c | 112 ++++++ hw/arm/meson.build | 2 + include/hw/arm/fsl-imx8mm.h | 158 ++++++++ 8 files changed, 709 insertions(+), 14 deletions(-) rename docs/system/arm/{imx8mp-evk.rst => imx8m.rst} (58%) create mode 100644 hw/arm/fsl-imx8mm.c create mode 100644 hw/arm/imx8mm-evk.c create mode 100644 include/hw/arm/fsl-imx8mm.h diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ F: hw/pci-host/designware.c F: include/hw/pci-host/designware.h F: docs/system/arm/mcimx7d-sabre.rst +MCIMX8MM-EVK / iMX8MM +M: Gaurav Sharma <gaurav.sharma_7@nxp.com> +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/fsl-imx8mm.c +F: hw/arm/imx8mm-evk.c +F: include/hw/arm/fsl-imx8mm.h +F: docs/system/arm/imx8m.rst + MCIMX8MP-EVK / i.MX8MP M: Bernhard Beschow <shentey@gmail.com> L: qemu-arm@nongnu.org @@ -XXX,XX +XXX,XX @@ F: hw/rtc/rs5c372.c F: include/hw/arm/fsl-imx8mp.h F: include/hw/misc/imx8mp_*.h F: include/hw/pci-host/fsl_imx8m_phy.h -F: docs/system/arm/imx8mp-evk.rst +F: docs/system/arm/imx8m.rst F: tests/functional/aarch64/test_imx8mp_evk.py F: tests/qtest/rs5c372-test.c diff --git a/docs/system/arm/imx8mp-evk.rst b/docs/system/arm/imx8m.rst similarity index 58% rename from docs/system/arm/imx8mp-evk.rst rename to docs/system/arm/imx8m.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/arm/imx8mp-evk.rst +++ b/docs/system/arm/imx8m.rst @@ -XXX,XX +XXX,XX @@ -NXP i.MX 8M Plus Evaluation Kit (``imx8mp-evk``) -================================================ +NXP i.MX 8M Plus and i.MX 8M Mini Evaluation Kits (``imx8mp-evk``, ``imx8mm-evk``) +================================================================================== -The ``imx8mp-evk`` machine models the i.MX 8M Plus Evaluation Kit, based on an -i.MX 8M Plus SoC. +The ``imx8mp-evk`` and ``imx8mm-evk`` machine models the i.MX 8M Plus +and i.MX 8M Mini Evaluation Kits, based on i.MX 8M Plus and i.MX8M +Mini SoCs. Supported devices ----------------- -The ``imx8mp-evk`` machine implements the following devices: +The ``imx8mp-evk`` and ``imx8mm-evk`` machines implement the +following devices: * Up to 4 Cortex-A53 cores * Generic Interrupt Controller (GICv3) @@ -XXX,XX +XXX,XX @@ The ``imx8mp-evk`` machine implements the following devices: Boot options ------------ -The ``imx8mp-evk`` machine can start a Linux kernel directly using the standard -``-kernel`` functionality. +The ``imx8mp-evk`` and ``imx8mm-evk`` machines can start a Linux +kernel directly using the standard ``-kernel`` functionality. Direct Linux Kernel Boot '''''''''''''''''''''''' @@ -XXX,XX +XXX,XX @@ is to generate an image with Buildroot. Version 2024.11.1 is tested at the time of writing and involves two steps. First run the following commands in the toplevel directory of the Buildroot source tree: +For i.MX 8M Plus EVK: + .. code-block:: bash $ make freescale_imx8mpevk_defconfig $ make +For i.MX 8M Mini EVK: + +.. code-block:: bash + + $ make freescale_imx8mmevk_defconfig + $ make + Once finished successfully there is an ``output/image`` subfolder. Navigate into it and resize the SD card image to a power of two: @@ -XXX,XX +XXX,XX @@ it and resize the SD card image to a power of two: Now that everything is prepared the machine can be started as follows: +For i.MX 8M Plus EVK: + .. code-block:: bash $ qemu-system-aarch64 -M imx8mp-evk \ @@ -XXX,XX +XXX,XX @@ Now that everything is prepared the machine can be started as follows: -append "root=/dev/mmcblk2p2" \ -drive file=sdcard.img,if=sd,bus=2,format=raw,id=mmcblk2 +For i.MX 8M Mini EVK: + +.. code-block:: bash + + $ qemu-system-aarch64 -M imx8mm-evk -smp 4 -m 2G \ + -display none -serial null -serial stdio \ + -kernel Image \ + -dtb imx8mm-evk.dtb \ + -append "root=/dev/mmcblk2p2" \ + -drive file=sdcard.img,if=sd,bus=2,format=raw,id=mmcblk2 KVM Acceleration ---------------- @@ -XXX,XX +XXX,XX @@ To enable hardware-assisted acceleration via KVM, append ``-accel kvm`` to the command line. While this speeds up performance significantly, be aware of the following limitations: -* The ``imx8mp-evk`` machine is not included under the "virtualization use case" - of :doc:`QEMU's security policy </system/security>`. This means that you - should not trust that it can contain malicious guests, whether it is run - using TCG or KVM. If you don't trust your guests and you're relying on QEMU to - be the security boundary, you want to choose another machine such as ``virt``. +* The ``imx8mp-evk`` and ``imx8mm-evk`` machines are not included + under the "virtualization use case" of :doc:`QEMU's security + policy </system/security>`. This means that you should not trust that + it can contain malicious guests, whether it is run using TCG or KVM. + If you don't trust your guests and you're relying on QEMU to be the + security boundary, you want to choose another machine such as + ``virt``. * Rather than Cortex-A53 CPUs, the same CPU type as the host's will be used. This is a limitation of KVM and may not work with guests with a tight dependency on Cortex-A53. diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -XXX,XX +XXX,XX @@ Board-specific documentation arm/imx25-pdk arm/mcimx6ul-evk arm/mcimx7d-sabre - arm/imx8mp-evk + arm/imx8m arm/orangepi arm/raspi arm/collie diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MP_EVK depends on TCG select FSL_IMX8MP +config FSL_IMX8MM + bool + select ARM_GIC + select IMX + +config FSL_IMX8MM_EVK + bool + default y + depends on AARCH64 + depends on TCG + select FSL_IMX8MM + config ARM_SMMUV3_ACCEL bool depends on ARM_SMMUV3 diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ +/* + * i.MX 8MM SoC Implementation + * + * Based on hw/arm/fsl-imx6.c + * + * Copyright (c) 2025, NXP Semiconductors + * Author: Gaurav Sharma <gaurav.sharma_7@nxp.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * iMX8MM Reference Manual - https://www.nxp.com/products/i.MX8MMINI -> Documentation + */ + +#include "qemu/osdep.h" +#include "system/address-spaces.h" +#include "hw/arm/bsa.h" +#include "hw/arm/fsl-imx8mm.h" +#include "hw/misc/unimp.h" +#include "hw/core/boards.h" +#include "system/kvm.h" +#include "system/system.h" +#include "target/arm/cpu.h" +#include "target/arm/cpu-qom.h" +#include "target/arm/kvm_arm.h" +#include "qapi/error.h" +#include "qobject/qlist.h" + +static const struct { + hwaddr addr; + size_t size; + const char *name; +} fsl_imx8mm_memmap[] = { + [FSL_IMX8MM_RAM] = { FSL_IMX8MM_RAM_START, FSL_IMX8MM_RAM_SIZE_MAX, "ram" }, + [FSL_IMX8MM_DDR_PHY_BROADCAST] = { 0x3dc00000, 4 * MiB, "ddr_phy_broadcast" }, + [FSL_IMX8MM_DDR_PERF_MON] = { 0x3d800000, 4 * MiB, "ddr_perf_mon" }, + [FSL_IMX8MM_DDR_CTL] = { 0x3d400000, 4 * MiB, "ddr_ctl" }, + [FSL_IMX8MM_DDR_PHY] = { 0x3c000000, 16 * MiB, "ddr_phy" }, + [FSL_IMX8MM_GIC_DIST] = { 0x38800000, 512 * KiB, "gic_dist" }, + [FSL_IMX8MM_GIC_REDIST] = { 0x38880000, 512 * KiB, "gic_redist" }, + [FSL_IMX8MM_VPU] = { 0x38340000, 2 * MiB, "vpu" }, + [FSL_IMX8MM_VPU_BLK_CTRL] = { 0x38330000, 2 * MiB, "vpu_blk_ctrl" }, + [FSL_IMX8MM_VPU_G2_DECODER] = { 0x38310000, 1 * MiB, "vpu_g2_decoder" }, + [FSL_IMX8MM_VPU_G1_DECODER] = { 0x38300000, 1 * MiB, "vpu_g1_decoder" }, + [FSL_IMX8MM_USB2_OTG] = { 0x32e50200, 0x200, "usb2_otg" }, + [FSL_IMX8MM_USB2] = { 0x32e50000, 0x200, "usb2" }, + [FSL_IMX8MM_USB1_OTG] = { 0x32e40200, 0x200, "usb1_otg" }, + [FSL_IMX8MM_USB1] = { 0x32e40000, 0x200, "usb1" }, + [FSL_IMX8MM_GPU2D] = { 0x38000000, 64 * KiB, "gpu2d" }, + [FSL_IMX8MM_QSPI1_RX_BUFFER] = { 0x34000000, 32 * MiB, "qspi1_rx_buffer" }, + [FSL_IMX8MM_PCIE1] = { 0x33800000, 4 * MiB, "pcie1" }, + [FSL_IMX8MM_QSPI1_TX_BUFFER] = { 0x33008000, 32 * KiB, "qspi1_tx_buffer" }, + [FSL_IMX8MM_APBH_DMA] = { 0x33000000, 32 * KiB, "apbh_dma" }, + + /* AIPS-4 Begin */ + [FSL_IMX8MM_TZASC] = { 0x32f80000, 64 * KiB, "tzasc" }, + [FSL_IMX8MM_PCIE_PHY1] = { 0x32f00000, 64 * KiB, "pcie_phy1" }, + [FSL_IMX8MM_MEDIA_BLK_CTL] = { 0x32e28000, 256, "media_blk_ctl" }, + [FSL_IMX8MM_LCDIF] = { 0x32e00000, 64 * KiB, "lcdif" }, + [FSL_IMX8MM_MIPI_DSI] = { 0x32e10000, 64 * KiB, "mipi_dsi" }, + [FSL_IMX8MM_MIPI_CSI] = { 0x32e30000, 64 * KiB, "mipi_csi" }, + [FSL_IMX8MM_AIPS4_CONFIGURATION] = { 0x32df0000, 64 * KiB, "aips4_configuration" }, + /* AIPS-4 End */ + + [FSL_IMX8MM_INTERCONNECT] = { 0x32700000, 1 * MiB, "interconnect" }, + + /* AIPS-3 Begin */ + [FSL_IMX8MM_ENET1] = { 0x30be0000, 64 * KiB, "enet1" }, + [FSL_IMX8MM_SDMA1] = { 0x30bd0000, 64 * KiB, "sdma1" }, + [FSL_IMX8MM_QSPI] = { 0x30bb0000, 64 * KiB, "qspi" }, + [FSL_IMX8MM_USDHC3] = { 0x30b60000, 64 * KiB, "usdhc3" }, + [FSL_IMX8MM_USDHC2] = { 0x30b50000, 64 * KiB, "usdhc2" }, + [FSL_IMX8MM_USDHC1] = { 0x30b40000, 64 * KiB, "usdhc1" }, + [FSL_IMX8MM_SEMAPHORE_HS] = { 0x30ac0000, 64 * KiB, "semaphore_hs" }, + [FSL_IMX8MM_MU_B] = { 0x30ab0000, 64 * KiB, "mu_b" }, + [FSL_IMX8MM_MU_A] = { 0x30aa0000, 64 * KiB, "mu_a" }, + [FSL_IMX8MM_UART4] = { 0x30a60000, 64 * KiB, "uart4" }, + [FSL_IMX8MM_I2C4] = { 0x30a50000, 64 * KiB, "i2c4" }, + [FSL_IMX8MM_I2C3] = { 0x30a40000, 64 * KiB, "i2c3" }, + [FSL_IMX8MM_I2C2] = { 0x30a30000, 64 * KiB, "i2c2" }, + [FSL_IMX8MM_I2C1] = { 0x30a20000, 64 * KiB, "i2c1" }, + [FSL_IMX8MM_AIPS3_CONFIGURATION] = { 0x309f0000, 64 * KiB, "aips3_configuration" }, + [FSL_IMX8MM_CAAM] = { 0x30900000, 256 * KiB, "caam" }, + [FSL_IMX8MM_SPBA1] = { 0x308f0000, 64 * KiB, "spba1" }, + [FSL_IMX8MM_UART2] = { 0x30890000, 64 * KiB, "uart2" }, + [FSL_IMX8MM_UART3] = { 0x30880000, 64 * KiB, "uart3" }, + [FSL_IMX8MM_UART1] = { 0x30860000, 64 * KiB, "uart1" }, + [FSL_IMX8MM_ECSPI3] = { 0x30840000, 64 * KiB, "ecspi3" }, + [FSL_IMX8MM_ECSPI2] = { 0x30830000, 64 * KiB, "ecspi2" }, + [FSL_IMX8MM_ECSPI1] = { 0x30820000, 64 * KiB, "ecspi1" }, + /* AIPS-3 End */ + + /* AIPS-2 Begin */ + [FSL_IMX8MM_QOSC] = { 0x307f0000, 64 * KiB, "qosc" }, + [FSL_IMX8MM_PERFMON2] = { 0x307d0000, 64 * KiB, "perfmon2" }, + [FSL_IMX8MM_PERFMON1] = { 0x307c0000, 64 * KiB, "perfmon1" }, + [FSL_IMX8MM_GPT4] = { 0x30700000, 64 * KiB, "gpt4" }, + [FSL_IMX8MM_GPT5] = { 0x306f0000, 64 * KiB, "gpt5" }, + [FSL_IMX8MM_GPT6] = { 0x306e0000, 64 * KiB, "gpt6" }, + [FSL_IMX8MM_SYSCNT_CTRL] = { 0x306c0000, 64 * KiB, "syscnt_ctrl" }, + [FSL_IMX8MM_SYSCNT_CMP] = { 0x306b0000, 64 * KiB, "syscnt_cmp" }, + [FSL_IMX8MM_SYSCNT_RD] = { 0x306a0000, 64 * KiB, "syscnt_rd" }, + [FSL_IMX8MM_PWM4] = { 0x30690000, 64 * KiB, "pwm4" }, + [FSL_IMX8MM_PWM3] = { 0x30680000, 64 * KiB, "pwm3" }, + [FSL_IMX8MM_PWM2] = { 0x30670000, 64 * KiB, "pwm2" }, + [FSL_IMX8MM_PWM1] = { 0x30660000, 64 * KiB, "pwm1" }, + [FSL_IMX8MM_AIPS2_CONFIGURATION] = { 0x305f0000, 64 * KiB, "aips2_configuration" }, + /* AIPS-2 End */ + + /* AIPS-1 Begin */ + [FSL_IMX8MM_CSU] = { 0x303e0000, 64 * KiB, "csu" }, + [FSL_IMX8MM_RDC] = { 0x303d0000, 64 * KiB, "rdc" }, + [FSL_IMX8MM_SEMAPHORE2] = { 0x303c0000, 64 * KiB, "semaphore2" }, + [FSL_IMX8MM_SEMAPHORE1] = { 0x303b0000, 64 * KiB, "semaphore1" }, + [FSL_IMX8MM_GPC] = { 0x303a0000, 64 * KiB, "gpc" }, + [FSL_IMX8MM_SRC] = { 0x30390000, 64 * KiB, "src" }, + [FSL_IMX8MM_CCM] = { 0x30380000, 64 * KiB, "ccm" }, + [FSL_IMX8MM_SNVS_HP] = { 0x30370000, 64 * KiB, "snvs_hp" }, + [FSL_IMX8MM_ANA_PLL] = { 0x30360000, 64 * KiB, "ana_pll" }, + [FSL_IMX8MM_OCOTP_CTRL] = { 0x30350000, 64 * KiB, "ocotp_ctrl" }, + [FSL_IMX8MM_IOMUXC_GPR] = { 0x30340000, 64 * KiB, "iomuxc_gpr" }, + [FSL_IMX8MM_IOMUXC] = { 0x30330000, 64 * KiB, "iomuxc" }, + [FSL_IMX8MM_GPT3] = { 0x302f0000, 64 * KiB, "gpt3" }, + [FSL_IMX8MM_GPT2] = { 0x302e0000, 64 * KiB, "gpt2" }, + [FSL_IMX8MM_GPT1] = { 0x302d0000, 64 * KiB, "gpt1" }, + [FSL_IMX8MM_SDMA2] = { 0x302c0000, 64 * KiB, "sdma2" }, + [FSL_IMX8MM_SDMA3] = { 0x302b0000, 64 * KiB, "sdma3" }, + [FSL_IMX8MM_WDOG3] = { 0x302a0000, 64 * KiB, "wdog3" }, + [FSL_IMX8MM_WDOG2] = { 0x30290000, 64 * KiB, "wdog2" }, + [FSL_IMX8MM_WDOG1] = { 0x30280000, 64 * KiB, "wdog1" }, + [FSL_IMX8MM_ANA_OSC] = { 0x30270000, 64 * KiB, "ana_osc" }, + [FSL_IMX8MM_ANA_TSENSOR] = { 0x30260000, 64 * KiB, "ana_tsensor" }, + [FSL_IMX8MM_GPIO5] = { 0x30240000, 64 * KiB, "gpio5" }, + [FSL_IMX8MM_GPIO4] = { 0x30230000, 64 * KiB, "gpio4" }, + [FSL_IMX8MM_GPIO3] = { 0x30220000, 64 * KiB, "gpio3" }, + [FSL_IMX8MM_GPIO2] = { 0x30210000, 64 * KiB, "gpio2" }, + [FSL_IMX8MM_GPIO1] = { 0x30200000, 64 * KiB, "gpio1" }, + [FSL_IMX8MM_AIPS1_CONFIGURATION] = { 0x301f0000, 64 * KiB, "aips1_configuration" }, + [FSL_IMX8MM_SAI6] = { 0x30060000, 64 * KiB, "sai6" }, + [FSL_IMX8MM_SAI5] = { 0x30050000, 64 * KiB, "sai5" }, + [FSL_IMX8MM_SAI3] = { 0x30030000, 64 * KiB, "sai3" }, + [FSL_IMX8MM_SAI2] = { 0x30020000, 64 * KiB, "sai2" }, + [FSL_IMX8MM_SAI1] = { 0x30010000, 64 * KiB, "sai1" }, + + /* AIPS-1 End */ + + [FSL_IMX8MM_A53_DAP] = { 0x28000000, 16 * MiB, "a53_dap" }, + [FSL_IMX8MM_PCIE1_MEM] = { 0x18000000, 128 * MiB, "pcie1_mem" }, + [FSL_IMX8MM_QSPI_MEM] = { 0x08000000, 256 * MiB, "qspi_mem" }, + [FSL_IMX8MM_OCRAM] = { 0x00900000, 256 * KiB, "ocram" }, + [FSL_IMX8MM_TCM_DTCM] = { 0x00800000, 128 * KiB, "tcm_dtcm" }, + [FSL_IMX8MM_TCM_ITCM] = { 0x007e0000, 128 * KiB, "tcm_itcm" }, + [FSL_IMX8MM_OCRAM_S] = { 0x00180000, 32 * KiB, "ocram_s" }, + [FSL_IMX8MM_CAAM_MEM] = { 0x00100000, 32 * KiB, "caam_mem" }, + [FSL_IMX8MM_BOOT_ROM_PROTECTED] = { 0x0003f000, 4 * KiB, "boot_rom_protected" }, + [FSL_IMX8MM_BOOT_ROM] = { 0x00000000, 252 * KiB, "boot_rom" }, +}; + +static void fsl_imx8mm_init(Object *obj) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + FslImx8mmState *s = FSL_IMX8MM(obj); + const char *cpu_type = ms->cpu_type ?: ARM_CPU_TYPE_NAME("cortex-a53"); + int i; + + for (i = 0; i < MIN(ms->smp.cpus, FSL_IMX8MM_NUM_CPUS); i++) { + g_autofree char *name = g_strdup_printf("cpu%d", i); + object_initialize_child(obj, name, &s->cpu[i], cpu_type); + } + + object_initialize_child(obj, "gic", &s->gic, gicv3_class_name()); + + for (i = 0; i < FSL_IMX8MM_NUM_UARTS; i++) { + g_autofree char *name = g_strdup_printf("uart%d", i + 1); + object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); + } + +} + +static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + FslImx8mmState *s = FSL_IMX8MM(dev); + DeviceState *gicdev = DEVICE(&s->gic); + int i; + + if (ms->smp.cpus > FSL_IMX8MM_NUM_CPUS) { + error_setg(errp, "%s: Only %d CPUs are supported (%d requested)", + TYPE_FSL_IMX8MM, FSL_IMX8MM_NUM_CPUS, ms->smp.cpus); + return; + } + + /* CPUs */ + for (i = 0; i < ms->smp.cpus; i++) { + /* On uniprocessor, the CBAR is set to 0 */ + if (ms->smp.cpus > 1 && + object_property_find(OBJECT(&s->cpu[i]), "reset-cbar")) { + object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar", + fsl_imx8mm_memmap[FSL_IMX8MM_GIC_DIST].addr, + &error_abort); + } + + /* + * CNTFID0 base frequency in Hz of system counter + */ + object_property_set_int(OBJECT(&s->cpu[i]), "cntfrq", 8000000, + &error_abort); + + if (object_property_find(OBJECT(&s->cpu[i]), "has_el2")) { + object_property_set_bool(OBJECT(&s->cpu[i]), "has_el2", + !kvm_enabled(), &error_abort); + } + + if (object_property_find(OBJECT(&s->cpu[i]), "has_el3")) { + object_property_set_bool(OBJECT(&s->cpu[i]), "has_el3", + !kvm_enabled(), &error_abort); + } + + if (i) { + /* + * Secondary CPUs start in powered-down state (and can be + * powered up via the SRC system reset controller) + */ + object_property_set_bool(OBJECT(&s->cpu[i]), "start-powered-off", + true, &error_abort); + } + + if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { + return; + } + } + + /* GIC */ + { + SysBusDevice *gicsbd = SYS_BUS_DEVICE(&s->gic); + QList *redist_region_count; + bool pmu = object_property_get_bool(OBJECT(first_cpu), "pmu", NULL); + + qdev_prop_set_uint32(gicdev, "num-cpu", ms->smp.cpus); + qdev_prop_set_uint32(gicdev, "num-irq", + FSL_IMX8MM_NUM_IRQS + GIC_INTERNAL); + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, ms->smp.cpus); + qdev_prop_set_array(gicdev, "redist-region-count", redist_region_count); + object_property_set_link(OBJECT(&s->gic), "sysmem", + OBJECT(get_system_memory()), &error_fatal); + if (!sysbus_realize(gicsbd, errp)) { + return; + } + sysbus_mmio_map(gicsbd, 0, fsl_imx8mm_memmap[FSL_IMX8MM_GIC_DIST].addr); + sysbus_mmio_map(gicsbd, 1, fsl_imx8mm_memmap[FSL_IMX8MM_GIC_REDIST].addr); + + /* + * Wire the outputs from each CPU's generic timer and the GICv3 + * maintenance interrupt signal to the appropriate GIC PPI inputs, and + * the GIC's IRQ/FIQ interrupt outputs to the CPU's inputs. + */ + for (i = 0; i < ms->smp.cpus; i++) { + DeviceState *cpudev = DEVICE(&s->cpu[i]); + int intidbase = FSL_IMX8MM_NUM_IRQS + i * GIC_INTERNAL; + qemu_irq irq; + + /* + * Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs. + */ + static const int timer_irqs[] = { + [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ, + [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, + [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, + [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, + }; + + for (int j = 0; j < ARRAY_SIZE(timer_irqs); j++) { + irq = qdev_get_gpio_in(gicdev, intidbase + timer_irqs[j]); + qdev_connect_gpio_out(cpudev, j, irq); + } + + irq = qdev_get_gpio_in(gicdev, intidbase + ARCH_GIC_MAINT_IRQ); + qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", + 0, irq); + + irq = qdev_get_gpio_in(gicdev, intidbase + VIRTUAL_PMU_IRQ); + qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, irq); + + sysbus_connect_irq(gicsbd, i, + qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(gicsbd, i + ms->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(gicsbd, i + 2 * ms->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(gicsbd, i + 3 * ms->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + + if (kvm_enabled()) { + if (pmu) { + assert(arm_feature(&s->cpu[i].env, ARM_FEATURE_PMU)); + if (kvm_irqchip_in_kernel()) { + kvm_arm_pmu_set_irq(&s->cpu[i], VIRTUAL_PMU_IRQ); + } + kvm_arm_pmu_init(&s->cpu[i]); + } + } + } + } + + /* UARTs */ + for (i = 0; i < FSL_IMX8MM_NUM_UARTS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } serial_table[FSL_IMX8MM_NUM_UARTS] = { + { fsl_imx8mm_memmap[FSL_IMX8MM_UART1].addr, FSL_IMX8MM_UART1_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_UART2].addr, FSL_IMX8MM_UART2_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_UART3].addr, FSL_IMX8MM_UART3_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_UART4].addr, FSL_IMX8MM_UART4_IRQ }, + }; + + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + qdev_get_gpio_in(gicdev, serial_table[i].irq)); + } + + /* On-Chip RAM */ + if (!memory_region_init_ram(&s->ocram, OBJECT(dev), "imx8mm.ocram", + fsl_imx8mm_memmap[FSL_IMX8MM_OCRAM].size, + errp)) { + return; + } + memory_region_add_subregion(get_system_memory(), + fsl_imx8mm_memmap[FSL_IMX8MM_OCRAM].addr, + &s->ocram); + + /* Unimplemented devices */ + for (i = 0; i < ARRAY_SIZE(fsl_imx8mm_memmap); i++) { + switch (i) { + case FSL_IMX8MM_GIC_DIST: + case FSL_IMX8MM_GIC_REDIST: + case FSL_IMX8MM_RAM: + case FSL_IMX8MM_OCRAM: + case FSL_IMX8MM_UART1 ... FSL_IMX8MM_UART4: + /* device implemented and treated above */ + break; + + default: + create_unimplemented_device(fsl_imx8mm_memmap[i].name, + fsl_imx8mm_memmap[i].addr, + fsl_imx8mm_memmap[i].size); + break; + } + } +} + +static void fsl_imx8mm_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = fsl_imx8mm_realize; + + dc->desc = "i.MX 8MM SoC"; +} + +static const TypeInfo fsl_imx8mm_types[] = { + { + .name = TYPE_FSL_IMX8MM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(FslImx8mmState), + .instance_init = fsl_imx8mm_init, + .class_init = fsl_imx8mm_class_init, + }, +}; + +DEFINE_TYPES(fsl_imx8mm_types) diff --git a/hw/arm/imx8mm-evk.c b/hw/arm/imx8mm-evk.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/arm/imx8mm-evk.c @@ -XXX,XX +XXX,XX @@ +/* + * NXP i.MX 8MM Evaluation Kit System Emulation + * + * Copyright (c) 2025, NXP Semiconductors + * Author: Gaurav Sharma <gaurav.sharma_7@nxp.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/arm/fsl-imx8mm.h" +#include "hw/arm/machines-qom.h" +#include "hw/core/boards.h" +#include "hw/core/qdev-properties.h" +#include "system/kvm.h" +#include "system/qtest.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include <libfdt.h> + +static void imx8mm_evk_modify_dtb(const struct arm_boot_info *info, void *fdt) +{ + int i, offset; + + /* Temporarily disable following nodes until they are implemented */ + const char *nodes_to_remove[] = { + "nxp,imx8mm-fspi", + "fsl,imx8mm-mipi-csi", + "fsl,imx8mm-mipi-dsim" + }; + + for (i = 0; i < ARRAY_SIZE(nodes_to_remove); i++) { + const char *dev_str = nodes_to_remove[i]; + + offset = fdt_node_offset_by_compatible(fdt, -1, dev_str); + while (offset >= 0) { + fdt_nop_node(fdt, offset); + offset = fdt_node_offset_by_compatible(fdt, offset, dev_str); + } + } + + /* Remove cpu-idle-states property from CPU nodes */ + offset = fdt_node_offset_by_compatible(fdt, -1, "arm,cortex-a53"); + while (offset >= 0) { + fdt_nop_property(fdt, offset, "cpu-idle-states"); + offset = fdt_node_offset_by_compatible(fdt, offset, "arm,cortex-a53"); + } + + if (kvm_enabled()) { + /* Use system counter frequency from host CPU to fix time in guest */ + offset = fdt_node_offset_by_compatible(fdt, -1, "arm,armv8-timer"); + while (offset >= 0) { + fdt_nop_property(fdt, offset, "clock-frequency"); + offset = fdt_node_offset_by_compatible(fdt, offset, "arm,armv8-timer"); + } + } +} + +static void imx8mm_evk_init(MachineState *machine) +{ + static struct arm_boot_info boot_info; + FslImx8mmState *s; + + if (machine->ram_size > FSL_IMX8MM_RAM_SIZE_MAX) { + error_report("RAM size " RAM_ADDR_FMT " above max supported (%08" PRIx64 ")", + machine->ram_size, FSL_IMX8MM_RAM_SIZE_MAX); + exit(1); + } + + boot_info = (struct arm_boot_info) { + .loader_start = FSL_IMX8MM_RAM_START, + .board_id = -1, + .ram_size = machine->ram_size, + .psci_conduit = QEMU_PSCI_CONDUIT_SMC, + .modify_dtb = imx8mm_evk_modify_dtb, + }; + + s = FSL_IMX8MM(object_new_with_props(TYPE_FSL_IMX8MM, OBJECT(machine), + "soc", &error_fatal, NULL)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(s), &error_fatal); + + memory_region_add_subregion(get_system_memory(), FSL_IMX8MM_RAM_START, + machine->ram); + + if (!qtest_enabled()) { + arm_load_kernel(&s->cpu[0], machine, &boot_info); + } +} + +static const char *imx8mm_evk_get_default_cpu_type(const MachineState *ms) +{ + if (kvm_enabled()) { + return ARM_CPU_TYPE_NAME("host"); + } + + return ARM_CPU_TYPE_NAME("cortex-a53"); +} + +static void imx8mm_evk_machine_init(MachineClass *mc) +{ + mc->desc = "NXP i.MX 8MM EVK Board"; + mc->init = imx8mm_evk_init; + mc->max_cpus = FSL_IMX8MM_NUM_CPUS; + mc->default_cpus = FSL_IMX8MM_NUM_CPUS; + mc->default_ram_size = 2 * GiB; + mc->default_ram_id = "imx8mm-evk.ram"; + mc->get_default_cpu_type = imx8mm_evk_get_default_cpu_type; +} + +DEFINE_MACHINE_AARCH64("imx8mm-evk", imx8mm_evk_machine_init) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -XXX,XX +XXX,XX @@ arm_common_ss.add(when: 'CONFIG_ARMSSE', if_true: files('armsse.c')) arm_common_ss.add(when: 'CONFIG_FSL_IMX7', if_true: files('fsl-imx7.c', 'mcimx7d-sabre.c')) arm_common_ss.add(when: 'CONFIG_FSL_IMX8MP', if_true: files('fsl-imx8mp.c')) arm_common_ss.add(when: 'CONFIG_FSL_IMX8MP_EVK', if_true: files('imx8mp-evk.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX8MM', if_true: files('fsl-imx8mm.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX8MM_EVK', if_true: files('imx8mm-evk.c')) arm_common_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c')) arm_common_ss.add(when: 'CONFIG_ARM_SMMUV3_ACCEL', if_true: files('smmuv3-accel.c')) stub_ss.add(files('smmuv3-accel-stubs.c')) diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ +/* + * i.MX 8MM SoC Definitions + * + * Copyright (c) 2025, NXP Semiconductors + * Author: Gaurav Sharma <gaurav.sharma_7@nxp.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FSL_IMX8MM_H +#define FSL_IMX8MM_H + +#include "cpu.h" +#include "hw/char/imx_serial.h" +#include "hw/intc/arm_gicv3_common.h" +#include "qom/object.h" +#include "qemu/units.h" + +#define TYPE_FSL_IMX8MM "fsl-imx8mm" +OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mmState, FSL_IMX8MM) + +#define FSL_IMX8MM_RAM_START 0x40000000 +#define FSL_IMX8MM_RAM_SIZE_MAX (4 * GiB) + +enum FslImx8mmConfiguration { + FSL_IMX8MM_NUM_CPUS = 4, + FSL_IMX8MM_NUM_IRQS = 128, + FSL_IMX8MM_NUM_UARTS = 4, +}; + +struct FslImx8mmState { + SysBusDevice parent_obj; + + ARMCPU cpu[FSL_IMX8MM_NUM_CPUS]; + GICv3State gic; + IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; + MemoryRegion ocram; +}; + +enum FslImx8mmMemoryRegions { + FSL_IMX8MM_A53_DAP, + FSL_IMX8MM_AIPS1_CONFIGURATION, + FSL_IMX8MM_AIPS2_CONFIGURATION, + FSL_IMX8MM_AIPS3_CONFIGURATION, + FSL_IMX8MM_AIPS4_CONFIGURATION, + FSL_IMX8MM_ANA_OSC, + FSL_IMX8MM_ANA_PLL, + FSL_IMX8MM_ANA_TSENSOR, + FSL_IMX8MM_APBH_DMA, + FSL_IMX8MM_BOOT_ROM, + FSL_IMX8MM_BOOT_ROM_PROTECTED, + FSL_IMX8MM_CAAM, + FSL_IMX8MM_CAAM_MEM, + FSL_IMX8MM_CCM, + FSL_IMX8MM_CSU, + FSL_IMX8MM_DDR_CTL, + FSL_IMX8MM_DDR_PERF_MON, + FSL_IMX8MM_DDR_PHY, + FSL_IMX8MM_DDR_PHY_BROADCAST, + FSL_IMX8MM_ECSPI1, + FSL_IMX8MM_ECSPI2, + FSL_IMX8MM_ECSPI3, + FSL_IMX8MM_ENET1, + FSL_IMX8MM_GIC_DIST, + FSL_IMX8MM_GIC_REDIST, + FSL_IMX8MM_GPC, + FSL_IMX8MM_GPIO1, + FSL_IMX8MM_GPIO2, + FSL_IMX8MM_GPIO3, + FSL_IMX8MM_GPIO4, + FSL_IMX8MM_GPIO5, + FSL_IMX8MM_GPT1, + FSL_IMX8MM_GPT2, + FSL_IMX8MM_GPT3, + FSL_IMX8MM_GPT4, + FSL_IMX8MM_GPT5, + FSL_IMX8MM_GPT6, + FSL_IMX8MM_GPU2D, + FSL_IMX8MM_I2C1, + FSL_IMX8MM_I2C2, + FSL_IMX8MM_I2C3, + FSL_IMX8MM_I2C4, + FSL_IMX8MM_INTERCONNECT, + FSL_IMX8MM_IOMUXC, + FSL_IMX8MM_IOMUXC_GPR, + FSL_IMX8MM_MEDIA_BLK_CTL, + FSL_IMX8MM_LCDIF, + FSL_IMX8MM_MIPI_CSI, + FSL_IMX8MM_MIPI_DSI, + FSL_IMX8MM_MU_A, + FSL_IMX8MM_MU_B, + FSL_IMX8MM_OCOTP_CTRL, + FSL_IMX8MM_OCRAM, + FSL_IMX8MM_OCRAM_S, + FSL_IMX8MM_PCIE1, + FSL_IMX8MM_PCIE1_MEM, + FSL_IMX8MM_PCIE_PHY1, + FSL_IMX8MM_PERFMON1, + FSL_IMX8MM_PERFMON2, + FSL_IMX8MM_PWM1, + FSL_IMX8MM_PWM2, + FSL_IMX8MM_PWM3, + FSL_IMX8MM_PWM4, + FSL_IMX8MM_QOSC, + FSL_IMX8MM_QSPI, + FSL_IMX8MM_QSPI1_RX_BUFFER, + FSL_IMX8MM_QSPI1_TX_BUFFER, + FSL_IMX8MM_QSPI_MEM, + FSL_IMX8MM_RAM, + FSL_IMX8MM_RDC, + FSL_IMX8MM_SAI1, + FSL_IMX8MM_SAI2, + FSL_IMX8MM_SAI3, + FSL_IMX8MM_SAI5, + FSL_IMX8MM_SAI6, + FSL_IMX8MM_SDMA1, + FSL_IMX8MM_SDMA2, + FSL_IMX8MM_SDMA3, + FSL_IMX8MM_SEMAPHORE1, + FSL_IMX8MM_SEMAPHORE2, + FSL_IMX8MM_SEMAPHORE_HS, + FSL_IMX8MM_SNVS_HP, + FSL_IMX8MM_SPBA1, + FSL_IMX8MM_SRC, + FSL_IMX8MM_SYSCNT_CMP, + FSL_IMX8MM_SYSCNT_CTRL, + FSL_IMX8MM_SYSCNT_RD, + FSL_IMX8MM_TCM_DTCM, + FSL_IMX8MM_TCM_ITCM, + FSL_IMX8MM_TZASC, + FSL_IMX8MM_UART1, + FSL_IMX8MM_UART2, + FSL_IMX8MM_UART3, + FSL_IMX8MM_UART4, + FSL_IMX8MM_USB1, + FSL_IMX8MM_USB2, + FSL_IMX8MM_USB1_OTG, + FSL_IMX8MM_USB2_OTG, + FSL_IMX8MM_USDHC1, + FSL_IMX8MM_USDHC2, + FSL_IMX8MM_USDHC3, + FSL_IMX8MM_VPU, + FSL_IMX8MM_VPU_BLK_CTRL, + FSL_IMX8MM_VPU_G1_DECODER, + FSL_IMX8MM_VPU_G2_DECODER, + FSL_IMX8MM_WDOG1, + FSL_IMX8MM_WDOG2, + FSL_IMX8MM_WDOG3, +}; + +enum FslImx8mmIrqs { + FSL_IMX8MM_UART1_IRQ = 26, + FSL_IMX8MM_UART2_IRQ = 27, + FSL_IMX8MM_UART3_IRQ = 28, + FSL_IMX8MM_UART4_IRQ = 29, +}; + +#endif /* FSL_IMX8MM_H */ -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> Add configurable properties for register reset values that differ between i.MX 8M variants (Plus, Mini, etc.). This allows the same device implementation to be shared across multiple SoCs. Properties added: - arm-pll-fdiv-ctl0-reset: ARM PLL divider control reset value Default value is set to match i.MX 8MP reset value (0x000FA031). This can be overridden in the variant like iMX8MM with its own reset value. Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/misc/imx8mp_analog.c | 12 +++++++++++- include/hw/misc/imx8mp_analog.h | 3 +++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/hw/misc/imx8mp_analog.c b/hw/misc/imx8mp_analog.c index XXXXXXX..XXXXXXX 100644 --- a/hw/misc/imx8mp_analog.c +++ b/hw/misc/imx8mp_analog.c @@ -XXX,XX +XXX,XX @@ #include "qemu/log.h" #include "hw/misc/imx8mp_analog.h" +#include "hw/core/qdev-properties.h" #include "migration/vmstate.h" #define ANALOG_PLL_LOCK BIT(31) @@ -XXX,XX +XXX,XX @@ static void imx8mp_analog_reset(DeviceState *dev) s->analog[ANALOG_VPU_PLL_LOCKD_CTRL] = 0x0010003f; s->analog[ANALOG_VPU_PLL_MNIT_CTRL] = 0x00280081; s->analog[ANALOG_ARM_PLL_GEN_CTRL] = 0x00000810; - s->analog[ANALOG_ARM_PLL_FDIV_CTL0] = 0x000fa031; + + /* Use property value instead of hardcoded */ + s->analog[ANALOG_ARM_PLL_FDIV_CTL0] = s->arm_pll_fdiv_ctl0_reset; + s->analog[ANALOG_ARM_PLL_LOCKD_CTRL] = 0x0010003f; s->analog[ANALOG_ARM_PLL_MNIT_CTRL] = 0x00280081; s->analog[ANALOG_SYS_PLL1_GEN_CTRL] = 0x0aaaa810; @@ -XXX,XX +XXX,XX @@ static const VMStateDescription imx8mp_analog_vmstate = { }, }; +static const Property imx8mp_analog_properties[] = { + DEFINE_PROP_UINT32("arm-pll-fdiv-ctl0-reset", IMX8MPAnalogState, + arm_pll_fdiv_ctl0_reset, 0x000fa031), /* imx8mp default */ +}; + static void imx8mp_analog_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_legacy_reset(dc, imx8mp_analog_reset); + device_class_set_props(dc, imx8mp_analog_properties); dc->vmsd = &imx8mp_analog_vmstate; dc->desc = "i.MX 8M Plus Analog Module"; } diff --git a/include/hw/misc/imx8mp_analog.h b/include/hw/misc/imx8mp_analog.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/misc/imx8mp_analog.h +++ b/include/hw/misc/imx8mp_analog.h @@ -XXX,XX +XXX,XX @@ struct IMX8MPAnalogState { } mmio; uint32_t analog[ANALOG_MAX]; + + /* Property for variant-specific reset values */ + uint32_t arm_pll_fdiv_ctl0_reset; }; #endif /* IMX8MP_ANALOG_H */ -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> Add the Analog IP to i.MX8MM SoC. iMX8MM and i.MX8MP uses the same Analog IP so the analog ip source will be shared. The ARM PLL divider control register (arm-pll-fdiv-ctl0) has a different reset value on i.MX8MM (0x000fa030) compared to i.MX8MP (0x000fa031). So iMX8MM will be overriding this property with its own reset-value. Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mm.c | 12 ++++++++++++ include/hw/arm/fsl-imx8mm.h | 2 ++ 3 files changed, 15 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MP_EVK config FSL_IMX8MM bool select ARM_GIC + select FSL_IMX8MP_ANALOG select IMX config FSL_IMX8MM_EVK diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, "gic", &s->gic, gicv3_class_name()); + object_initialize_child(obj, "analog", &s->analog, TYPE_IMX8MP_ANALOG); + for (i = 0; i < FSL_IMX8MM_NUM_UARTS; i++) { g_autofree char *name = g_strdup_printf("uart%d", i + 1); object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) } } + /* Analog */ + object_property_set_uint(OBJECT(&s->analog), "arm-pll-fdiv-ctl0-reset", + 0x000fa030, &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->analog), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->analog), 0, + fsl_imx8mm_memmap[FSL_IMX8MM_ANA_PLL].addr); + /* UARTs */ for (i = 0; i < FSL_IMX8MM_NUM_UARTS; i++) { static const struct { @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) /* Unimplemented devices */ for (i = 0; i < ARRAY_SIZE(fsl_imx8mm_memmap); i++) { switch (i) { + case FSL_IMX8MM_ANA_PLL: case FSL_IMX8MM_GIC_DIST: case FSL_IMX8MM_GIC_REDIST: case FSL_IMX8MM_RAM: diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "cpu.h" #include "hw/char/imx_serial.h" #include "hw/intc/arm_gicv3_common.h" +#include "hw/misc/imx8mp_analog.h" #include "qom/object.h" #include "qemu/units.h" @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { ARMCPU cpu[FSL_IMX8MM_NUM_CPUS]; GICv3State gic; + IMX8MPAnalogState analog; IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; }; -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> Add the Clock Control Module (CCM) device to i.MX8MM SoC. The CCM implementation is shared with i.MX8MP as the register layout is identical between the two variants.Hence iMX8MM will be using the source of iMX8MP CCM. Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mm.c | 10 ++++++++++ include/hw/arm/fsl-imx8mm.h | 2 ++ 3 files changed, 13 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MM bool select ARM_GIC select FSL_IMX8MP_ANALOG + select FSL_IMX8MP_CCM select IMX config FSL_IMX8MM_EVK diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, "gic", &s->gic, gicv3_class_name()); + object_initialize_child(obj, "ccm", &s->ccm, TYPE_IMX8MP_CCM); + object_initialize_child(obj, "analog", &s->analog, TYPE_IMX8MP_ANALOG); for (i = 0; i < FSL_IMX8MM_NUM_UARTS; i++) { @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) } } + /* CCM */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ccm), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, + fsl_imx8mm_memmap[FSL_IMX8MM_CCM].addr); + /* Analog */ object_property_set_uint(OBJECT(&s->analog), "arm-pll-fdiv-ctl0-reset", 0x000fa030, &error_abort); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) for (i = 0; i < ARRAY_SIZE(fsl_imx8mm_memmap); i++) { switch (i) { case FSL_IMX8MM_ANA_PLL: + case FSL_IMX8MM_CCM: case FSL_IMX8MM_GIC_DIST: case FSL_IMX8MM_GIC_REDIST: case FSL_IMX8MM_RAM: diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "hw/char/imx_serial.h" #include "hw/intc/arm_gicv3_common.h" #include "hw/misc/imx8mp_analog.h" +#include "hw/misc/imx8mp_ccm.h" #include "qom/object.h" #include "qemu/units.h" @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { ARMCPU cpu[FSL_IMX8MM_NUM_CPUS]; GICv3State gic; + IMX8MPCCMState ccm; IMX8MPAnalogState analog; IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> SNVS contains an RTC which allows Linux to deal correctly with time Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/fsl-imx8mm.c | 10 ++++++++++ include/hw/arm/fsl-imx8mm.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, "analog", &s->analog, TYPE_IMX8MP_ANALOG); + object_initialize_child(obj, "snvs", &s->snvs, TYPE_IMX7_SNVS); + for (i = 0; i < FSL_IMX8MM_NUM_UARTS; i++) { g_autofree char *name = g_strdup_printf("uart%d", i + 1); object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) fsl_imx8mm_memmap[FSL_IMX8MM_OCRAM].addr, &s->ocram); + /* SNVS */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->snvs), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, + fsl_imx8mm_memmap[FSL_IMX8MM_SNVS_HP].addr); + /* Unimplemented devices */ for (i = 0; i < ARRAY_SIZE(fsl_imx8mm_memmap); i++) { switch (i) { @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_GIC_REDIST: case FSL_IMX8MM_RAM: case FSL_IMX8MM_OCRAM: + case FSL_IMX8MM_SNVS_HP: case FSL_IMX8MM_UART1 ... FSL_IMX8MM_UART4: /* device implemented and treated above */ break; diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "cpu.h" #include "hw/char/imx_serial.h" #include "hw/intc/arm_gicv3_common.h" +#include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" #include "hw/misc/imx8mp_ccm.h" #include "qom/object.h" @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { GICv3State gic; IMX8MPCCMState ccm; IMX8MPAnalogState analog; + IMX7SNVSState snvs; IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; }; -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> It enables emulation of SD/MMC cards through a virtual SDHCI interface The emulated SDHCI controller allows guest OS to use emulated storage as a standard block device. This will allow running the images such as those generated by Buildroot. Reviewed-by: Philippe Mathieu-Daude <philmd@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mm.c | 25 +++++++++++++++++++++++++ hw/arm/imx8mm-evk.c | 17 +++++++++++++++++ include/hw/arm/fsl-imx8mm.h | 7 +++++++ 4 files changed, 50 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MM select FSL_IMX8MP_ANALOG select FSL_IMX8MP_CCM select IMX + select SDHCI config FSL_IMX8MM_EVK bool diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); } + for (i = 0; i < FSL_IMX8MM_NUM_USDHCS; i++) { + g_autofree char *name = g_strdup_printf("usdhc%d", i + 1); + object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); + } } static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) fsl_imx8mm_memmap[FSL_IMX8MM_OCRAM].addr, &s->ocram); + /* USDHCs */ + for (i = 0; i < FSL_IMX8MM_NUM_USDHCS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } usdhc_table[FSL_IMX8MM_NUM_USDHCS] = { + { fsl_imx8mm_memmap[FSL_IMX8MM_USDHC1].addr, FSL_IMX8MM_USDHC1_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_USDHC2].addr, FSL_IMX8MM_USDHC2_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_USDHC3].addr, FSL_IMX8MM_USDHC3_IRQ }, + }; + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->usdhc[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0, usdhc_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0, + qdev_get_gpio_in(gicdev, usdhc_table[i].irq)); + } + /* SNVS */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->snvs), errp)) { return; @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_OCRAM: case FSL_IMX8MM_SNVS_HP: case FSL_IMX8MM_UART1 ... FSL_IMX8MM_UART4: + case FSL_IMX8MM_USDHC1 ... FSL_IMX8MM_USDHC3: /* device implemented and treated above */ break; diff --git a/hw/arm/imx8mm-evk.c b/hw/arm/imx8mm-evk.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/imx8mm-evk.c +++ b/hw/arm/imx8mm-evk.c @@ -XXX,XX +XXX,XX @@ static void imx8mm_evk_init(MachineState *machine) memory_region_add_subregion(get_system_memory(), FSL_IMX8MM_RAM_START, machine->ram); + for (int i = 0; i < FSL_IMX8MM_NUM_USDHCS; i++) { + BusState *bus; + DeviceState *carddev; + BlockBackend *blk; + DriveInfo *di = drive_get(IF_SD, i, 0); + + if (!di) { + continue; + } + + blk = blk_by_legacy_dinfo(di); + bus = qdev_get_child_bus(DEVICE(&s->usdhc[i]), "sd-bus"); + carddev = qdev_new(TYPE_SD_CARD); + qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); + qdev_realize_and_unref(carddev, bus, &error_fatal); + } + if (!qtest_enabled()) { arm_load_kernel(&s->cpu[0], machine, &boot_info); } diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" #include "hw/misc/imx8mp_ccm.h" +#include "hw/sd/sdhci.h" #include "qom/object.h" #include "qemu/units.h" @@ -XXX,XX +XXX,XX @@ enum FslImx8mmConfiguration { FSL_IMX8MM_NUM_CPUS = 4, FSL_IMX8MM_NUM_IRQS = 128, FSL_IMX8MM_NUM_UARTS = 4, + FSL_IMX8MM_NUM_USDHCS = 3, }; struct FslImx8mmState { @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { IMX7SNVSState snvs; IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; + SDHCIState usdhc[FSL_IMX8MM_NUM_USDHCS]; }; enum FslImx8mmMemoryRegions { @@ -XXX,XX +XXX,XX @@ enum FslImx8mmMemoryRegions { }; enum FslImx8mmIrqs { + FSL_IMX8MM_USDHC1_IRQ = 22, + FSL_IMX8MM_USDHC2_IRQ = 23, + FSL_IMX8MM_USDHC3_IRQ = 24, + FSL_IMX8MM_UART1_IRQ = 26, FSL_IMX8MM_UART2_IRQ = 27, FSL_IMX8MM_UART3_IRQ = 28, -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> This enables support for Designware PCI Express Controller emulation It provides a controlled environment to debug the linux pci subsystem Reviewed-by: Philippe Mathieu-Daude <philmd@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 3 +++ hw/arm/fsl-imx8mm.c | 30 ++++++++++++++++++++++++++++++ include/hw/arm/fsl-imx8mm.h | 10 ++++++++++ 3 files changed, 43 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MP_EVK config FSL_IMX8MM bool + imply PCI_DEVICES select ARM_GIC select FSL_IMX8MP_ANALOG select FSL_IMX8MP_CCM select IMX select SDHCI + select PCI_EXPRESS_DESIGNWARE + select PCI_EXPRESS_FSL_IMX8M_PHY config FSL_IMX8MM_EVK bool diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) g_autofree char *name = g_strdup_printf("usdhc%d", i + 1); object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); } + + object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); + object_initialize_child(obj, "pcie_phy", &s->pcie_phy, + TYPE_FSL_IMX8M_PCIE_PHY); } static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, fsl_imx8mm_memmap[FSL_IMX8MM_SNVS_HP].addr); + /* PCIe */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0, + fsl_imx8mm_memmap[FSL_IMX8MM_PCIE1].addr); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0, + qdev_get_gpio_in(gicdev, FSL_IMX8MM_PCI_INTA_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1, + qdev_get_gpio_in(gicdev, FSL_IMX8MM_PCI_INTB_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2, + qdev_get_gpio_in(gicdev, FSL_IMX8MM_PCI_INTC_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, + qdev_get_gpio_in(gicdev, FSL_IMX8MM_PCI_INTD_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 4, + qdev_get_gpio_in(gicdev, FSL_IMX8MM_PCI_MSI_IRQ)); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie_phy), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie_phy), 0, + fsl_imx8mm_memmap[FSL_IMX8MM_PCIE_PHY1].addr); + /* Unimplemented devices */ for (i = 0; i < ARRAY_SIZE(fsl_imx8mm_memmap); i++) { switch (i) { @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_CCM: case FSL_IMX8MM_GIC_DIST: case FSL_IMX8MM_GIC_REDIST: + case FSL_IMX8MM_PCIE1: + case FSL_IMX8MM_PCIE_PHY1: case FSL_IMX8MM_RAM: case FSL_IMX8MM_OCRAM: case FSL_IMX8MM_SNVS_HP: diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" #include "hw/misc/imx8mp_ccm.h" +#include "hw/pci-host/designware.h" +#include "hw/pci-host/fsl_imx8m_phy.h" #include "hw/sd/sdhci.h" #include "qom/object.h" #include "qemu/units.h" @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; SDHCIState usdhc[FSL_IMX8MM_NUM_USDHCS]; + DesignwarePCIEHost pcie; + FslImx8mPciePhyState pcie_phy; }; enum FslImx8mmMemoryRegions { @@ -XXX,XX +XXX,XX @@ enum FslImx8mmIrqs { FSL_IMX8MM_UART2_IRQ = 27, FSL_IMX8MM_UART3_IRQ = 28, FSL_IMX8MM_UART4_IRQ = 29, + + FSL_IMX8MM_PCI_INTA_IRQ = 122, + FSL_IMX8MM_PCI_INTB_IRQ = 123, + FSL_IMX8MM_PCI_INTC_IRQ = 124, + FSL_IMX8MM_PCI_INTD_IRQ = 125, + FSL_IMX8MM_PCI_MSI_IRQ = 127, }; #endif /* FSL_IMX8MM_H */ -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> Enabled GPIO controller emulation Also updated the GPIO IRQ lines of iMX8MM Reviewed-by: Philippe Mathieu-Daude <philmd@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/fsl-imx8mm.c | 54 +++++++++++++++++++++++++++++++++++++ include/hw/arm/fsl-imx8mm.h | 14 ++++++++++ 2 files changed, 68 insertions(+) diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); } + for (i = 0; i < FSL_IMX8MM_NUM_GPIOS; i++) { + g_autofree char *name = g_strdup_printf("gpio%d", i + 1); + object_initialize_child(obj, name, &s->gpio[i], TYPE_IMX_GPIO); + } + for (i = 0; i < FSL_IMX8MM_NUM_USDHCS; i++) { g_autofree char *name = g_strdup_printf("usdhc%d", i + 1); object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) fsl_imx8mm_memmap[FSL_IMX8MM_OCRAM].addr, &s->ocram); + /* GPIOs */ + for (i = 0; i < FSL_IMX8MM_NUM_GPIOS; i++) { + static const struct { + hwaddr addr; + unsigned int irq_low; + unsigned int irq_high; + } gpio_table[FSL_IMX8MM_NUM_GPIOS] = { + { + fsl_imx8mm_memmap[FSL_IMX8MM_GPIO1].addr, + FSL_IMX8MM_GPIO1_LOW_IRQ, + FSL_IMX8MM_GPIO1_HIGH_IRQ + }, + { + fsl_imx8mm_memmap[FSL_IMX8MM_GPIO2].addr, + FSL_IMX8MM_GPIO2_LOW_IRQ, + FSL_IMX8MM_GPIO2_HIGH_IRQ + }, + { + fsl_imx8mm_memmap[FSL_IMX8MM_GPIO3].addr, + FSL_IMX8MM_GPIO3_LOW_IRQ, + FSL_IMX8MM_GPIO3_HIGH_IRQ + }, + { + fsl_imx8mm_memmap[FSL_IMX8MM_GPIO4].addr, + FSL_IMX8MM_GPIO4_LOW_IRQ, + FSL_IMX8MM_GPIO4_HIGH_IRQ + }, + { + fsl_imx8mm_memmap[FSL_IMX8MM_GPIO5].addr, + FSL_IMX8MM_GPIO5_LOW_IRQ, + FSL_IMX8MM_GPIO5_HIGH_IRQ + }, + }; + object_property_set_bool(OBJECT(&s->gpio[i]), "has-edge-sel", true, + &error_abort); + object_property_set_bool(OBJECT(&s->gpio[i]), "has-upper-pin-irq", + true, &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(gicdev, gpio_table[i].irq_low)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1, + qdev_get_gpio_in(gicdev, gpio_table[i].irq_high)); + } + /* USDHCs */ for (i = 0; i < FSL_IMX8MM_NUM_USDHCS; i++) { static const struct { @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_CCM: case FSL_IMX8MM_GIC_DIST: case FSL_IMX8MM_GIC_REDIST: + case FSL_IMX8MM_GPIO1 ... FSL_IMX8MM_GPIO5: case FSL_IMX8MM_PCIE1: case FSL_IMX8MM_PCIE_PHY1: case FSL_IMX8MM_RAM: diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "cpu.h" #include "hw/char/imx_serial.h" +#include "hw/gpio/imx_gpio.h" #include "hw/intc/arm_gicv3_common.h" #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" @@ -XXX,XX +XXX,XX @@ OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mmState, FSL_IMX8MM) enum FslImx8mmConfiguration { FSL_IMX8MM_NUM_CPUS = 4, + FSL_IMX8MM_NUM_GPIOS = 5, FSL_IMX8MM_NUM_IRQS = 128, FSL_IMX8MM_NUM_UARTS = 4, FSL_IMX8MM_NUM_USDHCS = 3, @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { ARMCPU cpu[FSL_IMX8MM_NUM_CPUS]; GICv3State gic; + IMXGPIOState gpio[FSL_IMX8MM_NUM_GPIOS]; IMX8MPCCMState ccm; IMX8MPAnalogState analog; IMX7SNVSState snvs; @@ -XXX,XX +XXX,XX @@ enum FslImx8mmIrqs { FSL_IMX8MM_UART3_IRQ = 28, FSL_IMX8MM_UART4_IRQ = 29, + FSL_IMX8MM_GPIO1_LOW_IRQ = 64, + FSL_IMX8MM_GPIO1_HIGH_IRQ = 65, + FSL_IMX8MM_GPIO2_LOW_IRQ = 66, + FSL_IMX8MM_GPIO2_HIGH_IRQ = 67, + FSL_IMX8MM_GPIO3_LOW_IRQ = 68, + FSL_IMX8MM_GPIO3_HIGH_IRQ = 69, + FSL_IMX8MM_GPIO4_LOW_IRQ = 70, + FSL_IMX8MM_GPIO4_HIGH_IRQ = 71, + FSL_IMX8MM_GPIO5_LOW_IRQ = 72, + FSL_IMX8MM_GPIO5_HIGH_IRQ = 73, + FSL_IMX8MM_PCI_INTA_IRQ = 122, FSL_IMX8MM_PCI_INTB_IRQ = 123, FSL_IMX8MM_PCI_INTC_IRQ = 124, -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> This can be used to test and debug I2C device drivers. Added I2C interrupts Reviewed-by: Philippe Mathieu-Daude <philmd@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 2 ++ hw/arm/fsl-imx8mm.c | 27 +++++++++++++++++++++++++++ include/hw/arm/fsl-imx8mm.h | 8 ++++++++ 3 files changed, 37 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MP_EVK config FSL_IMX8MM bool imply PCI_DEVICES + imply I2C_DEVICES select ARM_GIC select FSL_IMX8MP_ANALOG select FSL_IMX8MP_CCM select IMX + select IMX_I2C select SDHCI select PCI_EXPRESS_DESIGNWARE select PCI_EXPRESS_FSL_IMX8M_PHY diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); } + for (i = 0; i < FSL_IMX8MM_NUM_I2CS; i++) { + g_autofree char *name = g_strdup_printf("i2c%d", i + 1); + object_initialize_child(obj, name, &s->i2c[i], TYPE_IMX_I2C); + } + for (i = 0; i < FSL_IMX8MM_NUM_GPIOS; i++) { g_autofree char *name = g_strdup_printf("gpio%d", i + 1); object_initialize_child(obj, name, &s->gpio[i], TYPE_IMX_GPIO); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) fsl_imx8mm_memmap[FSL_IMX8MM_OCRAM].addr, &s->ocram); + /* I2Cs */ + for (i = 0; i < FSL_IMX8MM_NUM_I2CS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } i2c_table[FSL_IMX8MM_NUM_I2CS] = { + { fsl_imx8mm_memmap[FSL_IMX8MM_I2C1].addr, FSL_IMX8MM_I2C1_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_I2C2].addr, FSL_IMX8MM_I2C2_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_I2C3].addr, FSL_IMX8MM_I2C3_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_I2C4].addr, FSL_IMX8MM_I2C4_IRQ }, + }; + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, + qdev_get_gpio_in(gicdev, i2c_table[i].irq)); + } + /* GPIOs */ for (i = 0; i < FSL_IMX8MM_NUM_GPIOS; i++) { static const struct { @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_GIC_DIST: case FSL_IMX8MM_GIC_REDIST: case FSL_IMX8MM_GPIO1 ... FSL_IMX8MM_GPIO5: + case FSL_IMX8MM_I2C1 ... FSL_IMX8MM_I2C4: case FSL_IMX8MM_PCIE1: case FSL_IMX8MM_PCIE_PHY1: case FSL_IMX8MM_RAM: diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "cpu.h" #include "hw/char/imx_serial.h" #include "hw/gpio/imx_gpio.h" +#include "hw/i2c/imx_i2c.h" #include "hw/intc/arm_gicv3_common.h" #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" @@ -XXX,XX +XXX,XX @@ OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mmState, FSL_IMX8MM) enum FslImx8mmConfiguration { FSL_IMX8MM_NUM_CPUS = 4, FSL_IMX8MM_NUM_GPIOS = 5, + FSL_IMX8MM_NUM_I2CS = 4, FSL_IMX8MM_NUM_IRQS = 128, FSL_IMX8MM_NUM_UARTS = 4, FSL_IMX8MM_NUM_USDHCS = 3, @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { IMX8MPCCMState ccm; IMX8MPAnalogState analog; IMX7SNVSState snvs; + IMXI2CState i2c[FSL_IMX8MM_NUM_I2CS]; IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; SDHCIState usdhc[FSL_IMX8MM_NUM_USDHCS]; @@ -XXX,XX +XXX,XX @@ enum FslImx8mmIrqs { FSL_IMX8MM_UART3_IRQ = 28, FSL_IMX8MM_UART4_IRQ = 29, + FSL_IMX8MM_I2C1_IRQ = 35, + FSL_IMX8MM_I2C2_IRQ = 36, + FSL_IMX8MM_I2C3_IRQ = 37, + FSL_IMX8MM_I2C4_IRQ = 38, + FSL_IMX8MM_GPIO1_LOW_IRQ = 64, FSL_IMX8MM_GPIO1_HIGH_IRQ = 65, FSL_IMX8MM_GPIO2_LOW_IRQ = 66, -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> It enables emulation of ECSPI in iMX8MM Added SPI IRQ lines Reviewed-by: Philippe Mathieu-Daude <philmd@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/fsl-imx8mm.c | 26 ++++++++++++++++++++++++++ include/hw/arm/fsl-imx8mm.h | 7 +++++++ 2 files changed, 33 insertions(+) diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); } + for (i = 0; i < FSL_IMX8MM_NUM_ECSPIS; i++) { + g_autofree char *name = g_strdup_printf("spi%d", i + 1); + object_initialize_child(obj, name, &s->spi[i], TYPE_IMX_SPI); + } + object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); object_initialize_child(obj, "pcie_phy", &s->pcie_phy, TYPE_FSL_IMX8M_PCIE_PHY); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, usdhc_table[i].irq)); } + /* ECSPIs */ + for (i = 0; i < FSL_IMX8MM_NUM_ECSPIS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } spi_table[FSL_IMX8MM_NUM_ECSPIS] = { + { fsl_imx8mm_memmap[FSL_IMX8MM_ECSPI1].addr, FSL_IMX8MM_ECSPI1_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_ECSPI2].addr, FSL_IMX8MM_ECSPI2_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_ECSPI3].addr, FSL_IMX8MM_ECSPI3_IRQ }, + }; + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, spi_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, + qdev_get_gpio_in(gicdev, spi_table[i].irq)); + } + /* SNVS */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->snvs), errp)) { return; @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_GIC_DIST: case FSL_IMX8MM_GIC_REDIST: case FSL_IMX8MM_GPIO1 ... FSL_IMX8MM_GPIO5: + case FSL_IMX8MM_ECSPI1 ... FSL_IMX8MM_ECSPI3: case FSL_IMX8MM_I2C1 ... FSL_IMX8MM_I2C4: case FSL_IMX8MM_PCIE1: case FSL_IMX8MM_PCIE_PHY1: diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "hw/pci-host/designware.h" #include "hw/pci-host/fsl_imx8m_phy.h" #include "hw/sd/sdhci.h" +#include "hw/ssi/imx_spi.h" #include "qom/object.h" #include "qemu/units.h" @@ -XXX,XX +XXX,XX @@ OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mmState, FSL_IMX8MM) enum FslImx8mmConfiguration { FSL_IMX8MM_NUM_CPUS = 4, + FSL_IMX8MM_NUM_ECSPIS = 3, FSL_IMX8MM_NUM_GPIOS = 5, FSL_IMX8MM_NUM_I2CS = 4, FSL_IMX8MM_NUM_IRQS = 128, @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { IMX8MPCCMState ccm; IMX8MPAnalogState analog; IMX7SNVSState snvs; + IMXSPIState spi[FSL_IMX8MM_NUM_ECSPIS]; IMXI2CState i2c[FSL_IMX8MM_NUM_I2CS]; IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; @@ -XXX,XX +XXX,XX @@ enum FslImx8mmIrqs { FSL_IMX8MM_UART3_IRQ = 28, FSL_IMX8MM_UART4_IRQ = 29, + FSL_IMX8MM_ECSPI1_IRQ = 31, + FSL_IMX8MM_ECSPI2_IRQ = 32, + FSL_IMX8MM_ECSPI3_IRQ = 33, + FSL_IMX8MM_I2C1_IRQ = 35, FSL_IMX8MM_I2C2_IRQ = 36, FSL_IMX8MM_I2C3_IRQ = 37, -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> It enables emulation of WDT in iMX8MM Added WDT IRQ lines Reviewed-by: Philippe Mathieu-Daude <philmd@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mm.c | 28 ++++++++++++++++++++++++++++ include/hw/arm/fsl-imx8mm.h | 7 +++++++ 3 files changed, 36 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MM select SDHCI select PCI_EXPRESS_DESIGNWARE select PCI_EXPRESS_FSL_IMX8M_PHY + select WDT_IMX2 config FSL_IMX8MM_EVK bool diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, name, &s->spi[i], TYPE_IMX_SPI); } + for (i = 0; i < FSL_IMX8MM_NUM_WDTS; i++) { + g_autofree char *name = g_strdup_printf("wdt%d", i); + object_initialize_child(obj, name, &s->wdt[i], TYPE_IMX2_WDT); + } + object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); object_initialize_child(obj, "pcie_phy", &s->pcie_phy, TYPE_FSL_IMX8M_PCIE_PHY); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, fsl_imx8mm_memmap[FSL_IMX8MM_SNVS_HP].addr); + /* Watchdogs */ + for (i = 0; i < FSL_IMX8MM_NUM_WDTS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } wdog_table[FSL_IMX8MM_NUM_WDTS] = { + { fsl_imx8mm_memmap[FSL_IMX8MM_WDOG1].addr, FSL_IMX8MM_WDOG1_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_WDOG2].addr, FSL_IMX8MM_WDOG2_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_WDOG3].addr, FSL_IMX8MM_WDOG3_IRQ }, + }; + + object_property_set_bool(OBJECT(&s->wdt[i]), "pretimeout-support", + true, &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, wdog_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[i]), 0, + qdev_get_gpio_in(gicdev, wdog_table[i].irq)); + } + /* PCIe */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie), errp)) { return; @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_SNVS_HP: case FSL_IMX8MM_UART1 ... FSL_IMX8MM_UART4: case FSL_IMX8MM_USDHC1 ... FSL_IMX8MM_USDHC3: + case FSL_IMX8MM_WDOG1 ... FSL_IMX8MM_WDOG3: /* device implemented and treated above */ break; diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "hw/pci-host/fsl_imx8m_phy.h" #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" +#include "hw/watchdog/wdt_imx2.h" #include "qom/object.h" #include "qemu/units.h" @@ -XXX,XX +XXX,XX @@ enum FslImx8mmConfiguration { FSL_IMX8MM_NUM_IRQS = 128, FSL_IMX8MM_NUM_UARTS = 4, FSL_IMX8MM_NUM_USDHCS = 3, + FSL_IMX8MM_NUM_WDTS = 3, }; struct FslImx8mmState { @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; SDHCIState usdhc[FSL_IMX8MM_NUM_USDHCS]; + IMX2WdtState wdt[FSL_IMX8MM_NUM_WDTS]; DesignwarePCIEHost pcie; FslImx8mPciePhyState pcie_phy; }; @@ -XXX,XX +XXX,XX @@ enum FslImx8mmIrqs { FSL_IMX8MM_GPIO5_LOW_IRQ = 72, FSL_IMX8MM_GPIO5_HIGH_IRQ = 73, + FSL_IMX8MM_WDOG1_IRQ = 78, + FSL_IMX8MM_WDOG2_IRQ = 79, + FSL_IMX8MM_WDOG3_IRQ = 10, + FSL_IMX8MM_PCI_INTA_IRQ = 122, FSL_IMX8MM_PCI_INTB_IRQ = 123, FSL_IMX8MM_PCI_INTC_IRQ = 124, -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> It enables emulation of GPT in iMX8MM Added GPT IRQ lines Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mm.c | 54 +++++++++++++++++++++++++++++++++++++ hw/timer/imx_gpt.c | 26 ++++++++++++++++++ include/hw/arm/fsl-imx8mm.h | 11 ++++++++ include/hw/timer/imx_gpt.h | 2 ++ 5 files changed, 94 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MM select FSL_IMX8MP_CCM select IMX select IMX_I2C + select OR_IRQ select SDHCI select PCI_EXPRESS_DESIGNWARE select PCI_EXPRESS_FSL_IMX8M_PHY diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); } + for (i = 0; i < FSL_IMX8MM_NUM_GPTS; i++) { + g_autofree char *name = g_strdup_printf("gpt%d", i + 1); + object_initialize_child(obj, name, &s->gpt[i], TYPE_IMX8MM_GPT); + } + object_initialize_child(obj, "gpt5-gpt6-irq", &s->gpt5_gpt6_irq, + TYPE_OR_IRQ); + for (i = 0; i < FSL_IMX8MM_NUM_I2CS; i++) { g_autofree char *name = g_strdup_printf("i2c%d", i + 1); object_initialize_child(obj, name, &s->i2c[i], TYPE_IMX_I2C); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) fsl_imx8mm_memmap[FSL_IMX8MM_OCRAM].addr, &s->ocram); + /* GPTs */ + object_property_set_int(OBJECT(&s->gpt5_gpt6_irq), "num-lines", 2, + &error_abort); + if (!qdev_realize(DEVICE(&s->gpt5_gpt6_irq), NULL, errp)) { + return; + } + + qdev_connect_gpio_out(DEVICE(&s->gpt5_gpt6_irq), 0, + qdev_get_gpio_in(gicdev, FSL_IMX8MM_GPT5_GPT6_IRQ)); + + for (i = 0; i < FSL_IMX8MM_NUM_GPTS; i++) { + hwaddr gpt_addrs[FSL_IMX8MM_NUM_GPTS] = { + fsl_imx8mm_memmap[FSL_IMX8MM_GPT1].addr, + fsl_imx8mm_memmap[FSL_IMX8MM_GPT2].addr, + fsl_imx8mm_memmap[FSL_IMX8MM_GPT3].addr, + fsl_imx8mm_memmap[FSL_IMX8MM_GPT4].addr, + fsl_imx8mm_memmap[FSL_IMX8MM_GPT5].addr, + fsl_imx8mm_memmap[FSL_IMX8MM_GPT6].addr, + }; + + s->gpt[i].ccm = IMX_CCM(&s->ccm); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpt[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, gpt_addrs[i]); + + if (i < FSL_IMX8MM_NUM_GPTS - 2) { + static const unsigned int gpt_irqs[FSL_IMX8MM_NUM_GPTS - 2] = { + FSL_IMX8MM_GPT1_IRQ, + FSL_IMX8MM_GPT2_IRQ, + FSL_IMX8MM_GPT3_IRQ, + FSL_IMX8MM_GPT4_IRQ, + }; + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, + qdev_get_gpio_in(gicdev, gpt_irqs[i])); + } else { + int irq = i - FSL_IMX8MM_NUM_GPTS + 2; + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, + qdev_get_gpio_in(DEVICE(&s->gpt5_gpt6_irq), irq)); + } + } + /* I2Cs */ for (i = 0; i < FSL_IMX8MM_NUM_I2CS; i++) { static const struct { @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_GIC_DIST: case FSL_IMX8MM_GIC_REDIST: case FSL_IMX8MM_GPIO1 ... FSL_IMX8MM_GPIO5: + case FSL_IMX8MM_GPT1 ... FSL_IMX8MM_GPT6: case FSL_IMX8MM_ECSPI1 ... FSL_IMX8MM_ECSPI3: case FSL_IMX8MM_I2C1 ... FSL_IMX8MM_I2C4: case FSL_IMX8MM_PCIE1: diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -XXX,XX +XXX,XX @@ * Originally written by Hans Jiang * Updated by Peter Chubb * Updated by Jean-Christophe Dubois <jcd@tribudubois.net> + * Updated by Gaurav Sharma <gaurav.sharma_7@nxp.com> * * This code is licensed under GPL version 2 or later. See * the COPYING file in the top-level directory. @@ -XXX,XX +XXX,XX @@ static const IMXClk imx8mp_gpt_clocks[] = { CLK_NONE, /* 111 not defined */ }; +static const IMXClk imx8mm_gpt_clocks[] = { + CLK_NONE, /* 000 No clock source */ + CLK_IPG, /* 001 ipg_clk, 532MHz */ + CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ + CLK_EXT, /* 011 External clock */ + CLK_32k, /* 100 ipg_clk_32k */ + CLK_HIGH, /* 101 ipg_clk_16M */ + CLK_NONE, /* 110 not defined */ + CLK_NONE, /* 111 not defined */ +}; + /* Must be called from within ptimer_transaction_begin/commit block */ static void imx_gpt_set_freq(IMXGPTState *s) { @@ -XXX,XX +XXX,XX @@ static void imx8mp_gpt_init(Object *obj) s->clocks = imx8mp_gpt_clocks; } +static void imx8mm_gpt_init(Object *obj) +{ + IMXGPTState *s = IMX_GPT(obj); + + s->clocks = imx8mm_gpt_clocks; +} + static const TypeInfo imx25_gpt_info = { .name = TYPE_IMX25_GPT, .parent = TYPE_SYS_BUS_DEVICE, @@ -XXX,XX +XXX,XX @@ static const TypeInfo imx8mp_gpt_info = { .instance_init = imx8mp_gpt_init, }; +static const TypeInfo imx8mm_gpt_info = { + .name = TYPE_IMX8MM_GPT, + .parent = TYPE_IMX25_GPT, + .instance_init = imx8mm_gpt_init, +}; + static void imx_gpt_register_types(void) { type_register_static(&imx25_gpt_info); @@ -XXX,XX +XXX,XX @@ static void imx_gpt_register_types(void) type_register_static(&imx6ul_gpt_info); type_register_static(&imx7_gpt_info); type_register_static(&imx8mp_gpt_info); + type_register_static(&imx8mm_gpt_info); } type_init(imx_gpt_register_types) diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" #include "hw/misc/imx8mp_ccm.h" +#include "hw/or-irq.h" #include "hw/pci-host/designware.h" #include "hw/pci-host/fsl_imx8m_phy.h" #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" +#include "hw/timer/imx_gpt.h" #include "hw/watchdog/wdt_imx2.h" #include "qom/object.h" #include "qemu/units.h" @@ -XXX,XX +XXX,XX @@ enum FslImx8mmConfiguration { FSL_IMX8MM_NUM_CPUS = 4, FSL_IMX8MM_NUM_ECSPIS = 3, FSL_IMX8MM_NUM_GPIOS = 5, + FSL_IMX8MM_NUM_GPTS = 6, FSL_IMX8MM_NUM_I2CS = 4, FSL_IMX8MM_NUM_IRQS = 128, FSL_IMX8MM_NUM_UARTS = 4, @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { ARMCPU cpu[FSL_IMX8MM_NUM_CPUS]; GICv3State gic; + IMXGPTState gpt[FSL_IMX8MM_NUM_GPTS]; IMXGPIOState gpio[FSL_IMX8MM_NUM_GPIOS]; IMX8MPCCMState ccm; IMX8MPAnalogState analog; @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { IMX2WdtState wdt[FSL_IMX8MM_NUM_WDTS]; DesignwarePCIEHost pcie; FslImx8mPciePhyState pcie_phy; + OrIRQState gpt5_gpt6_irq; }; enum FslImx8mmMemoryRegions { @@ -XXX,XX +XXX,XX @@ enum FslImx8mmIrqs { FSL_IMX8MM_I2C3_IRQ = 37, FSL_IMX8MM_I2C4_IRQ = 38, + FSL_IMX8MM_GPT1_IRQ = 55, + FSL_IMX8MM_GPT2_IRQ = 54, + FSL_IMX8MM_GPT3_IRQ = 53, + FSL_IMX8MM_GPT4_IRQ = 52, + FSL_IMX8MM_GPT5_GPT6_IRQ = 51, + FSL_IMX8MM_GPIO1_LOW_IRQ = 64, FSL_IMX8MM_GPIO1_HIGH_IRQ = 65, FSL_IMX8MM_GPIO2_LOW_IRQ = 66, diff --git a/include/hw/timer/imx_gpt.h b/include/hw/timer/imx_gpt.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/timer/imx_gpt.h +++ b/include/hw/timer/imx_gpt.h @@ -XXX,XX +XXX,XX @@ * Originally written by Hans Jiang * Updated by Peter Chubb * Updated by Jean-Christophe Dubois <jcd@tribudubois.net> + * Updated by Gaurav Sharma <gaurav.sharma_7@nxp.com> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -XXX,XX +XXX,XX @@ #define TYPE_IMX6UL_GPT "imx6ul.gpt" #define TYPE_IMX7_GPT "imx7.gpt" #define TYPE_IMX8MP_GPT "imx8mp.gpt" +#define TYPE_IMX8MM_GPT "imx8mm.gpt" #define TYPE_IMX_GPT TYPE_IMX25_GPT -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> It enables emulation of ENET ethernet controller in iMX8MM Enables testing and debugging of network dependent drivers Added ENET MAC IRQ lines Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mm.c | 24 ++++++++++++++++++++++++ hw/arm/imx8mm-evk.c | 1 + include/hw/arm/fsl-imx8mm.h | 10 +++++++++- 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MM select FSL_IMX8MP_ANALOG select FSL_IMX8MP_CCM select IMX + select IMX_FEC select IMX_I2C select OR_IRQ select SDHCI diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, name, &s->wdt[i], TYPE_IMX2_WDT); } + object_initialize_child(obj, "eth0", &s->enet, TYPE_IMX_ENET); + object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); object_initialize_child(obj, "pcie_phy", &s->pcie_phy, TYPE_FSL_IMX8M_PCIE_PHY); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, spi_table[i].irq)); } + /* ENET1 */ + object_property_set_uint(OBJECT(&s->enet), "phy-num", s->phy_num, + &error_abort); + object_property_set_uint(OBJECT(&s->enet), "tx-ring-num", 3, &error_abort); + qemu_configure_nic_device(DEVICE(&s->enet), true, NULL); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->enet), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->enet), 0, + fsl_imx8mm_memmap[FSL_IMX8MM_ENET1].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->enet), 0, + qdev_get_gpio_in(gicdev, FSL_IMX8MM_ENET1_MAC_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->enet), 1, + qdev_get_gpio_in(gicdev, FSL_IMX6_ENET1_MAC_1588_IRQ)); + /* SNVS */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->snvs), errp)) { return; @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_GPIO1 ... FSL_IMX8MM_GPIO5: case FSL_IMX8MM_GPT1 ... FSL_IMX8MM_GPT6: case FSL_IMX8MM_ECSPI1 ... FSL_IMX8MM_ECSPI3: + case FSL_IMX8MM_ENET1: case FSL_IMX8MM_I2C1 ... FSL_IMX8MM_I2C4: case FSL_IMX8MM_PCIE1: case FSL_IMX8MM_PCIE_PHY1: @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) } } +static const Property fsl_imx8mm_properties[] = { + DEFINE_PROP_UINT32("fec1-phy-num", FslImx8mmState, phy_num, 0), + DEFINE_PROP_BOOL("fec1-phy-connected", FslImx8mmState, phy_connected, true), +}; + static void fsl_imx8mm_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + device_class_set_props(dc, fsl_imx8mm_properties); dc->realize = fsl_imx8mm_realize; dc->desc = "i.MX 8MM SoC"; diff --git a/hw/arm/imx8mm-evk.c b/hw/arm/imx8mm-evk.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/imx8mm-evk.c +++ b/hw/arm/imx8mm-evk.c @@ -XXX,XX +XXX,XX @@ static void imx8mm_evk_init(MachineState *machine) s = FSL_IMX8MM(object_new_with_props(TYPE_FSL_IMX8MM, OBJECT(machine), "soc", &error_fatal, NULL)); + object_property_set_uint(OBJECT(s), "fec1-phy-num", 1, &error_fatal); sysbus_realize_and_unref(SYS_BUS_DEVICE(s), &error_fatal); memory_region_add_subregion(get_system_memory(), FSL_IMX8MM_RAM_START, diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" #include "hw/misc/imx8mp_ccm.h" -#include "hw/or-irq.h" +#include "hw/net/imx_fec.h" +#include "hw/core/or-irq.h" #include "hw/pci-host/designware.h" #include "hw/pci-host/fsl_imx8m_phy.h" #include "hw/sd/sdhci.h" @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { IMXI2CState i2c[FSL_IMX8MM_NUM_I2CS]; IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; + IMXFECState enet; SDHCIState usdhc[FSL_IMX8MM_NUM_USDHCS]; IMX2WdtState wdt[FSL_IMX8MM_NUM_WDTS]; DesignwarePCIEHost pcie; FslImx8mPciePhyState pcie_phy; OrIRQState gpt5_gpt6_irq; + + uint32_t phy_num; + bool phy_connected; }; enum FslImx8mmMemoryRegions { @@ -XXX,XX +XXX,XX @@ enum FslImx8mmIrqs { FSL_IMX8MM_WDOG2_IRQ = 79, FSL_IMX8MM_WDOG3_IRQ = 10, + FSL_IMX8MM_ENET1_MAC_IRQ = 118, + FSL_IMX6_ENET1_MAC_1588_IRQ = 121, + FSL_IMX8MM_PCI_INTA_IRQ = 122, FSL_IMX8MM_PCI_INTB_IRQ = 123, FSL_IMX8MM_PCI_INTC_IRQ = 124, -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> It enables emulation of USB on iMX8MM Enables testing and debugging of USB drivers Reviewed-by: Philippe Mathieu-Daude <philmd@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mm.c | 27 +++++++++++++++++++++++++++ include/hw/arm/fsl-imx8mm.h | 6 ++++++ 3 files changed, 34 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MM select SDHCI select PCI_EXPRESS_DESIGNWARE select PCI_EXPRESS_FSL_IMX8M_PHY + select USB_DWC3 select WDT_IMX2 config FSL_IMX8MM_EVK diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); } + for (i = 0; i < FSL_IMX8MM_NUM_USBS; i++) { + g_autofree char *name = g_strdup_printf("usb%d", i); + object_initialize_child(obj, name, &s->usb[i], TYPE_USB_DWC3); + } + for (i = 0; i < FSL_IMX8MM_NUM_ECSPIS; i++) { g_autofree char *name = g_strdup_printf("spi%d", i + 1); object_initialize_child(obj, name, &s->spi[i], TYPE_IMX_SPI); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, usdhc_table[i].irq)); } + /* USBs */ + for (i = 0; i < FSL_IMX8MM_NUM_USBS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } usb_table[FSL_IMX8MM_NUM_USBS] = { + { fsl_imx8mm_memmap[FSL_IMX8MM_USB1].addr, FSL_IMX8MM_USB1_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_USB2].addr, FSL_IMX8MM_USB2_IRQ }, + }; + + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "p2", 1); + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "p3", 1); + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "slots", 2); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->usb[i]), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, usb_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i].sysbus_xhci), 0, + qdev_get_gpio_in(gicdev, usb_table[i].irq)); + } + /* ECSPIs */ for (i = 0; i < FSL_IMX8MM_NUM_ECSPIS; i++) { static const struct { @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_OCRAM: case FSL_IMX8MM_SNVS_HP: case FSL_IMX8MM_UART1 ... FSL_IMX8MM_UART4: + case FSL_IMX8MM_USB1 ... FSL_IMX8MM_USB2: case FSL_IMX8MM_USDHC1 ... FSL_IMX8MM_USDHC3: case FSL_IMX8MM_WDOG1 ... FSL_IMX8MM_WDOG3: /* device implemented and treated above */ diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" #include "hw/timer/imx_gpt.h" +#include "hw/usb/hcd-dwc3.h" #include "hw/watchdog/wdt_imx2.h" #include "qom/object.h" #include "qemu/units.h" @@ -XXX,XX +XXX,XX @@ enum FslImx8mmConfiguration { FSL_IMX8MM_NUM_I2CS = 4, FSL_IMX8MM_NUM_IRQS = 128, FSL_IMX8MM_NUM_UARTS = 4, + FSL_IMX8MM_NUM_USBS = 2, FSL_IMX8MM_NUM_USDHCS = 3, FSL_IMX8MM_NUM_WDTS = 3, }; @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { IMXFECState enet; SDHCIState usdhc[FSL_IMX8MM_NUM_USDHCS]; IMX2WdtState wdt[FSL_IMX8MM_NUM_WDTS]; + USBDWC3 usb[FSL_IMX8MM_NUM_USBS]; DesignwarePCIEHost pcie; FslImx8mPciePhyState pcie_phy; OrIRQState gpt5_gpt6_irq; @@ -XXX,XX +XXX,XX @@ enum FslImx8mmIrqs { FSL_IMX8MM_I2C3_IRQ = 37, FSL_IMX8MM_I2C4_IRQ = 38, + FSL_IMX8MM_USB1_IRQ = 40, + FSL_IMX8MM_USB2_IRQ = 41, + FSL_IMX8MM_GPT1_IRQ = 55, FSL_IMX8MM_GPT2_IRQ = 54, FSL_IMX8MM_GPT3_IRQ = 53, -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> Added script that would validate the iMX8MM emulation by checking the linux console log. If it succeeds, it will return:- ok 1 test_imx8mm_evk.Imx8mmEvkMachine.test_aarch64_imx8mm_evk_usdhc Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Reviewed-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Bernhard Beschow <shentey@gmail.com> [PMM: Add missing blank lines that flake8 wants] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- MAINTAINERS | 1 + tests/functional/aarch64/meson.build | 2 + tests/functional/aarch64/test_imx8mm_evk.py | 69 +++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100755 tests/functional/aarch64/test_imx8mm_evk.py diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ F: hw/arm/fsl-imx8mm.c F: hw/arm/imx8mm-evk.c F: include/hw/arm/fsl-imx8mm.h F: docs/system/arm/imx8m.rst +F: tests/functional/aarch64/test_imx8mm_evk.py MCIMX8MP-EVK / i.MX8MP M: Bernhard Beschow <shentey@gmail.com> diff --git a/tests/functional/aarch64/meson.build b/tests/functional/aarch64/meson.build index XXXXXXX..XXXXXXX 100644 --- a/tests/functional/aarch64/meson.build +++ b/tests/functional/aarch64/meson.build @@ -XXX,XX +XXX,XX @@ test_aarch64_timeouts = { 'aspeed_ast2700a2' : 600, 'aspeed_ast2700fc' : 600, 'device_passthrough' : 720, + 'imx8mm_evk' : 240, 'imx8mp_evk' : 240, 'raspi4' : 480, 'reverse_debug' : 180, @@ -XXX,XX +XXX,XX @@ tests_aarch64_system_thorough = [ 'aspeed_ast2700fc', 'device_passthrough', 'hotplug_pci', + 'imx8mm_evk', 'imx8mp_evk', 'kvm', 'multiprocess', diff --git a/tests/functional/aarch64/test_imx8mm_evk.py b/tests/functional/aarch64/test_imx8mm_evk.py new file mode 100755 index XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/functional/aarch64/test_imx8mm_evk.py @@ -XXX,XX +XXX,XX @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + + +class Imx8mmEvkMachine(LinuxKernelTest): + + ASSET_IMAGE = Asset( + ('https://cloud.debian.org/images/cloud/bookworm/20231210-1590/' + 'debian-12-generic-arm64-20231210-1590.tar.xz'), + '7ebf1577b32d5af6204df74b54ca2e4675de9b5a9fa14f3ff70b88eeb7b3b359') + + KERNEL_OFFSET = 0x51000000 + KERNEL_SIZE = 32622528 + INITRD_OFFSET = 0x76000000 + INITRD_SIZE = 30987766 + DTB_OFFSET = 0x64DB5000 + DTB_SIZE = 36812 + + def extract(self, in_path, out_path, offset, size): + try: + with open(in_path, "rb") as source: + source.seek(offset) + data = source.read(size) + with open(out_path, "wb") as target: + target.write(data) + except (IOError, ValueError) as e: + self.log.error(f"Failed to extract {out_path}: {e}") + raise + + def setUp(self): + super().setUp() + + self.image_path = self.scratch_file("disk.raw") + self.kernel_path = self.scratch_file("linux") + self.initrd_path = self.scratch_file("initrd.zstd") + self.dtb_path = self.scratch_file("imx8mm-evk.dtb") + + self.archive_extract(self.ASSET_IMAGE) + self.extract(self.image_path, self.kernel_path, + self.KERNEL_OFFSET, self.KERNEL_SIZE) + self.extract(self.image_path, self.initrd_path, + self.INITRD_OFFSET, self.INITRD_SIZE) + self.extract(self.image_path, self.dtb_path, + self.DTB_OFFSET, self.DTB_SIZE) + + def test_aarch64_imx8mm_evk_usdhc(self): + self.require_accelerator("tcg") + self.set_machine('imx8mm-evk') + self.vm.set_console(console_index=1) + self.vm.add_args('-m', '2G', + '-smp', '4', + '-kernel', self.kernel_path, + '-initrd', self.initrd_path, + '-dtb', self.dtb_path, + '-append', 'root=/dev/mmcblk2p1', + '-drive', f'file={self.image_path},if=sd,bus=2,' + 'format=raw,id=mmcblk2,snapshot=on') + + self.vm.launch() + self.wait_for_console_pattern('Welcome to ') + + +if __name__ == '__main__': + LinuxKernelTest.main() -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> As mentioned by Richard in [*]: We don't actually need any specific endianness here, because every byte has the same value. So we could simply drop MO_TE. That would produce a store in host-endianness, which will be fractionally more efficient on some hosts. [*] https://lore.kernel.org/qemu-devel/d64d3576-9188-4e74-afdc-3492c9feb8e0@linaro.org/ Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260414005348.4767-2-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/helper-a64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -XXX,XX +XXX,XX @@ static uint64_t set_step_tags(CPUARMState *env, uint64_t toaddr, * the page dirty and will use the fast path. */ uint64_t repldata = data * 0x0101010101010101ULL; - MemOpIdx oi16 = make_memop_idx(MO_TE | MO_128, memidx); + MemOpIdx oi16 = make_memop_idx(MO_128, memidx); cpu_st16_mmu(env, toaddr, int128_make128(repldata, repldata), oi16, ra); mte_mops_set_tags(env, toaddr, 16, *mtedesc); return 16; -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Extract the implicit MO_TE definition in order to replace it in the next commit. Mechanical change using: $ for n in UW UL UQ UO SW SL SQ; do \ sed -i -e "s/MO_TE$n/MO_TE | MO_$n/" \ $(git grep -l MO_TE$n target/arm); \ done Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260414005348.4767-3-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/m_helper.c | 6 +-- target/arm/tcg/mve_helper.c | 79 ++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 40 deletions(-) diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -XXX,XX +XXX,XX @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) /* Note that these stores can throw exceptions on MPU faults */ ARMMMUIdx mmu_idx = arm_mmu_idx(env); - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, arm_to_core_mmu_idx(mmu_idx)); cpu_stl_mmu(env, sp, nextinst, oi, GETPC()); cpu_stl_mmu(env, sp + 4, saved_psr, oi, GETPC()); @@ -XXX,XX +XXX,XX @@ void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) bool lspact = env->v7m.fpccr[s] & R_V7M_FPCCR_LSPACT_MASK; uintptr_t ra = GETPC(); ARMMMUIdx mmu_idx = arm_mmu_idx(env); - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, arm_to_core_mmu_idx(mmu_idx)); assert(env->v7m.secure); @@ -XXX,XX +XXX,XX @@ void HELPER(v7m_vlldm)(CPUARMState *env, uint32_t fptr) ARMCPU *cpu = env_archcpu(env); uintptr_t ra = GETPC(); ARMMMUIdx mmu_idx = arm_mmu_idx(env); - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, arm_to_core_mmu_idx(mmu_idx)); /* fptr is the value of Rn, the frame pointer we load the FP regs from */ diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -XXX,XX +XXX,XX @@ static void mve_advance_vpt(CPUARMState *env) } DO_VLDR(vldrb, MO_UB, 1, uint8_t, ldb, 1, uint8_t) -DO_VLDR(vldrh, MO_TEUW, 2, uint16_t, ldw, 2, uint16_t) -DO_VLDR(vldrw, MO_TEUL, 4, uint32_t, ldl, 4, uint32_t) +DO_VLDR(vldrh, MO_TE | MO_UW, 2, uint16_t, ldw, 2, uint16_t) +DO_VLDR(vldrw, MO_TE | MO_UL, 4, uint32_t, ldl, 4, uint32_t) DO_VSTR(vstrb, MO_UB, 1, stb, 1, uint8_t) -DO_VSTR(vstrh, MO_TEUW, 2, stw, 2, uint16_t) -DO_VSTR(vstrw, MO_TEUL, 4, stl, 4, uint32_t) +DO_VSTR(vstrh, MO_TE | MO_UW, 2, stw, 2, uint16_t) +DO_VSTR(vstrw, MO_TE | MO_UL, 4, stl, 4, uint32_t) DO_VLDR(vldrb_sh, MO_SB, 1, int8_t, ldb, 2, int16_t) DO_VLDR(vldrb_sw, MO_SB, 1, int8_t, ldb, 4, int32_t) DO_VLDR(vldrb_uh, MO_UB, 1, uint8_t, ldb, 2, uint16_t) DO_VLDR(vldrb_uw, MO_UB, 1, uint8_t, ldb, 4, uint32_t) -DO_VLDR(vldrh_sw, MO_TESW, 2, int16_t, ldw, 4, int32_t) -DO_VLDR(vldrh_uw, MO_TEUW, 2, uint16_t, ldw, 4, uint32_t) +DO_VLDR(vldrh_sw, MO_TE | MO_SW, 2, int16_t, ldw, 4, int32_t) +DO_VLDR(vldrh_uw, MO_TE | MO_UW, 2, uint16_t, ldw, 4, uint32_t) DO_VSTR(vstrb_h, MO_UB, 1, stb, 2, int16_t) DO_VSTR(vstrb_w, MO_UB, 1, stb, 4, int32_t) -DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) +DO_VSTR(vstrh_w, MO_TE | MO_UW, 2, stw, 4, int32_t) #undef DO_VLDR #undef DO_VSTR @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) unsigned e; \ uint32_t addr; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (e = 0; e < 16 / 4; e++, mask >>= 4, eci_mask >>= 4) { \ if (!(eci_mask & 1)) { \ continue; \ @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) unsigned e; \ uint32_t addr; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (e = 0; e < 16 / 4; e++, mask >>= 4, eci_mask >>= 4) { \ if (!(eci_mask & 1)) { \ continue; \ @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) DO_VLDR_SG(vldrb_sg_sh, MO_SB, int8_t, ldb, 2, int16_t, uint16_t, ADDR_ADD, false) DO_VLDR_SG(vldrb_sg_sw, MO_SB, int8_t, ldb, 4, int32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_sw, MO_TESW, int16_t, ldw, 4, int32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_sw, MO_TE | MO_SW, int16_t, ldw, 4, + int32_t, uint32_t, ADDR_ADD, false) DO_VLDR_SG(vldrb_sg_ub, MO_UB, uint8_t, ldb, 1, uint8_t, uint8_t, ADDR_ADD, false) DO_VLDR_SG(vldrb_sg_uh, MO_UB, uint8_t, ldb, 2, uint16_t, uint16_t, ADDR_ADD, false) DO_VLDR_SG(vldrb_sg_uw, MO_UB, uint8_t, ldb, 4, uint32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_uh, MO_TEUW, uint16_t, ldw, 2, uint16_t, uint16_t, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_uw, MO_TEUW, uint16_t, ldw, 4, uint32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrw_sg_uw, MO_TEUL, uint32_t, ldl, 4, uint32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_uh, MO_TE | MO_UW, uint16_t, ldw, 2, + uint16_t, uint16_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_uw, MO_TE | MO_UW, uint16_t, ldw, 4, + uint32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrw_sg_uw, MO_TE | MO_UL, uint32_t, ldl, 4, + uint32_t, uint32_t, ADDR_ADD, false) DO_VLDR64_SG(vldrd_sg_ud, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_os_sw, MO_TESW, int16_t, ldw, 4, +DO_VLDR_SG(vldrh_sg_os_sw, MO_TE | MO_SW, int16_t, ldw, 4, int32_t, uint32_t, ADDR_ADD_OSH, false) -DO_VLDR_SG(vldrh_sg_os_uh, MO_TEUW, uint16_t, ldw, 2, +DO_VLDR_SG(vldrh_sg_os_uh, MO_TE | MO_UW, uint16_t, ldw, 2, uint16_t, uint16_t, ADDR_ADD_OSH, false) -DO_VLDR_SG(vldrh_sg_os_uw, MO_TEUW, uint16_t, ldw, 4, +DO_VLDR_SG(vldrh_sg_os_uw, MO_TE | MO_UW, uint16_t, ldw, 4, uint32_t, uint32_t, ADDR_ADD_OSH, false) -DO_VLDR_SG(vldrw_sg_os_uw, MO_TEUL, uint32_t, ldl, 4, +DO_VLDR_SG(vldrw_sg_os_uw, MO_TE | MO_UL, uint32_t, ldl, 4, uint32_t, uint32_t, ADDR_ADD_OSW, false) DO_VLDR64_SG(vldrd_sg_os_ud, ADDR_ADD_OSD, false) DO_VSTR_SG(vstrb_sg_ub, MO_UB, stb, 1, uint8_t, ADDR_ADD, false) DO_VSTR_SG(vstrb_sg_uh, MO_UB, stb, 2, uint16_t, ADDR_ADD, false) DO_VSTR_SG(vstrb_sg_uw, MO_UB, stb, 4, uint32_t, ADDR_ADD, false) -DO_VSTR_SG(vstrh_sg_uh, MO_TEUW, stw, 2, uint16_t, ADDR_ADD, false) -DO_VSTR_SG(vstrh_sg_uw, MO_TEUW, stw, 4, uint32_t, ADDR_ADD, false) -DO_VSTR_SG(vstrw_sg_uw, MO_TEUL, stl, 4, uint32_t, ADDR_ADD, false) +DO_VSTR_SG(vstrh_sg_uh, MO_TE | MO_UW, stw, 2, uint16_t, ADDR_ADD, false) +DO_VSTR_SG(vstrh_sg_uw, MO_TE | MO_UW, stw, 4, uint32_t, ADDR_ADD, false) +DO_VSTR_SG(vstrw_sg_uw, MO_TE | MO_UL, stl, 4, uint32_t, ADDR_ADD, false) DO_VSTR64_SG(vstrd_sg_ud, ADDR_ADD, false) -DO_VSTR_SG(vstrh_sg_os_uh, MO_TEUW, stw, 2, uint16_t, ADDR_ADD_OSH, false) -DO_VSTR_SG(vstrh_sg_os_uw, MO_TEUW, stw, 4, uint32_t, ADDR_ADD_OSH, false) -DO_VSTR_SG(vstrw_sg_os_uw, MO_TEUL, stl, 4, uint32_t, ADDR_ADD_OSW, false) +DO_VSTR_SG(vstrh_sg_os_uh, MO_TE | MO_UW, stw, 2, uint16_t, ADDR_ADD_OSH, false) +DO_VSTR_SG(vstrh_sg_os_uw, MO_TE | MO_UW, stw, 4, uint32_t, ADDR_ADD_OSH, false) +DO_VSTR_SG(vstrw_sg_os_uw, MO_TE | MO_UL, stl, 4, uint32_t, ADDR_ADD_OSW, false) DO_VSTR64_SG(vstrd_sg_os_ud, ADDR_ADD_OSD, false) -DO_VLDR_SG(vldrw_sg_wb_uw, MO_TEUL, uint32_t, ldl, 4, uint32_t, uint32_t, ADDR_ADD, true) +DO_VLDR_SG(vldrw_sg_wb_uw, MO_TE | MO_UL, uint32_t, ldl, 4, + uint32_t, uint32_t, ADDR_ADD, true) DO_VLDR64_SG(vldrd_sg_wb_ud, ADDR_ADD, true) -DO_VSTR_SG(vstrw_sg_wb_uw, MO_TEUL, stl, 4, uint32_t, ADDR_ADD, true) +DO_VSTR_SG(vstrw_sg_wb_uw, MO_TE | MO_UL, stl, 4, uint32_t, ADDR_ADD, true) DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) /* @@ -XXX,XX +XXX,XX @@ DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) static const uint8_t off[4] = { O1, O2, O3, O4 }; \ uint32_t addr, data; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) int y; /* y counts 0 2 0 2 */ \ uint16_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0, y = 0; beat < 4; beat++, mask >>= 4, y ^= 2) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) uint32_t *qd; \ int y; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD4W(vld43w, 6, 7, 8, 9) uint32_t addr, data; \ uint8_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD4W(vld43w, 6, 7, 8, 9) int e; \ uint16_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD4W(vld43w, 6, 7, 8, 9) uint32_t addr, data; \ uint32_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD2W(vld21w, 8, 12, 16, 20) static const uint8_t off[4] = { O1, O2, O3, O4 }; \ uint32_t addr, data; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD2W(vld21w, 8, 12, 16, 20) int y; /* y counts 0 2 0 2 */ \ uint16_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0, y = 0; beat < 4; beat++, mask >>= 4, y ^= 2) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD2W(vld21w, 8, 12, 16, 20) uint32_t *qd; \ int y; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VST4W(vst43w, 6, 7, 8, 9) uint32_t addr, data; \ uint8_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VST4W(vst43w, 6, 7, 8, 9) int e; \ uint16_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VST4W(vst43w, 6, 7, 8, 9) uint32_t addr, data; \ uint32_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260414005348.4767-4-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/mve_helper.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -XXX,XX +XXX,XX @@ static void mve_advance_vpt(CPUARMState *env) uint16_t eci_mask = mve_eci_mask(env); \ unsigned b, e; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MFLAG | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MFLAG | MO_ALIGN, \ + mmu_idx); \ /* \ * R_SXTM allows the dest reg to become UNKNOWN for abandoned \ * beats so we don't care if we update part of the dest and \ @@ -XXX,XX +XXX,XX @@ static void mve_advance_vpt(CPUARMState *env) uint16_t mask = mve_element_mask(env); \ unsigned b, e; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MFLAG | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MFLAG | MO_ALIGN, \ + mmu_idx); \ for (b = 0, e = 0; b < 16; b += ESIZE, e++) { \ if (mask & (1 << b)) { \ cpu_##STTYPE##_mmu(env, addr, d[H##ESIZE(e)], oi, GETPC()); \ @@ -XXX,XX +XXX,XX @@ static void mve_advance_vpt(CPUARMState *env) } DO_VLDR(vldrb, MO_UB, 1, uint8_t, ldb, 1, uint8_t) -DO_VLDR(vldrh, MO_TE | MO_UW, 2, uint16_t, ldw, 2, uint16_t) -DO_VLDR(vldrw, MO_TE | MO_UL, 4, uint32_t, ldl, 4, uint32_t) +DO_VLDR(vldrh, MO_UW, 2, uint16_t, ldw, 2, uint16_t) +DO_VLDR(vldrw, MO_UL, 4, uint32_t, ldl, 4, uint32_t) DO_VSTR(vstrb, MO_UB, 1, stb, 1, uint8_t) -DO_VSTR(vstrh, MO_TE | MO_UW, 2, stw, 2, uint16_t) -DO_VSTR(vstrw, MO_TE | MO_UL, 4, stl, 4, uint32_t) +DO_VSTR(vstrh, MO_UW, 2, stw, 2, uint16_t) +DO_VSTR(vstrw, MO_UL, 4, stl, 4, uint32_t) DO_VLDR(vldrb_sh, MO_SB, 1, int8_t, ldb, 2, int16_t) DO_VLDR(vldrb_sw, MO_SB, 1, int8_t, ldb, 4, int32_t) DO_VLDR(vldrb_uh, MO_UB, 1, uint8_t, ldb, 2, uint16_t) DO_VLDR(vldrb_uw, MO_UB, 1, uint8_t, ldb, 4, uint32_t) -DO_VLDR(vldrh_sw, MO_TE | MO_SW, 2, int16_t, ldw, 4, int32_t) -DO_VLDR(vldrh_uw, MO_TE | MO_UW, 2, uint16_t, ldw, 4, uint32_t) +DO_VLDR(vldrh_sw, MO_SW, 2, int16_t, ldw, 4, int32_t) +DO_VLDR(vldrh_uw, MO_UW, 2, uint16_t, ldw, 4, uint32_t) DO_VSTR(vstrb_h, MO_UB, 1, stb, 2, int16_t) DO_VSTR(vstrb_w, MO_UB, 1, stb, 4, int32_t) -DO_VSTR(vstrh_w, MO_TE | MO_UW, 2, stw, 4, int32_t) +DO_VSTR(vstrh_w, MO_UW, 2, stw, 4, int32_t) #undef DO_VLDR #undef DO_VSTR -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260414005348.4767-5-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/mve_helper.c | 43 +++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_UW, 2, stw, 4, int32_t) unsigned e; \ uint32_t addr; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MFLAG | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MFLAG | MO_ALIGN, \ + mmu_idx); \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE, eci_mask >>= ESIZE) { \ if (!(eci_mask & 1)) { \ continue; \ @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_UW, 2, stw, 4, int32_t) unsigned e; \ uint32_t addr; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MFLAG | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MFLAG | MO_ALIGN, \ + mmu_idx); \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE, eci_mask >>= ESIZE) { \ if (!(eci_mask & 1)) { \ continue; \ @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_UW, 2, stw, 4, int32_t) DO_VLDR_SG(vldrb_sg_sh, MO_SB, int8_t, ldb, 2, int16_t, uint16_t, ADDR_ADD, false) DO_VLDR_SG(vldrb_sg_sw, MO_SB, int8_t, ldb, 4, int32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_sw, MO_TE | MO_SW, int16_t, ldw, 4, - int32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_sw, MO_SW, int16_t, ldw, 4, int32_t, uint32_t, ADDR_ADD, false) DO_VLDR_SG(vldrb_sg_ub, MO_UB, uint8_t, ldb, 1, uint8_t, uint8_t, ADDR_ADD, false) DO_VLDR_SG(vldrb_sg_uh, MO_UB, uint8_t, ldb, 2, uint16_t, uint16_t, ADDR_ADD, false) DO_VLDR_SG(vldrb_sg_uw, MO_UB, uint8_t, ldb, 4, uint32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_uh, MO_TE | MO_UW, uint16_t, ldw, 2, - uint16_t, uint16_t, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_uw, MO_TE | MO_UW, uint16_t, ldw, 4, - uint32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrw_sg_uw, MO_TE | MO_UL, uint32_t, ldl, 4, - uint32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_uh, MO_UW, uint16_t, ldw, 2, uint16_t, uint16_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_uw, MO_UW, uint16_t, ldw, 4, uint32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrw_sg_uw, MO_UL, uint32_t, ldl, 4, uint32_t, uint32_t, ADDR_ADD, false) DO_VLDR64_SG(vldrd_sg_ud, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_os_sw, MO_TE | MO_SW, int16_t, ldw, 4, +DO_VLDR_SG(vldrh_sg_os_sw, MO_SW, int16_t, ldw, 4, int32_t, uint32_t, ADDR_ADD_OSH, false) -DO_VLDR_SG(vldrh_sg_os_uh, MO_TE | MO_UW, uint16_t, ldw, 2, +DO_VLDR_SG(vldrh_sg_os_uh, MO_UW, uint16_t, ldw, 2, uint16_t, uint16_t, ADDR_ADD_OSH, false) -DO_VLDR_SG(vldrh_sg_os_uw, MO_TE | MO_UW, uint16_t, ldw, 4, +DO_VLDR_SG(vldrh_sg_os_uw, MO_UW, uint16_t, ldw, 4, uint32_t, uint32_t, ADDR_ADD_OSH, false) -DO_VLDR_SG(vldrw_sg_os_uw, MO_TE | MO_UL, uint32_t, ldl, 4, +DO_VLDR_SG(vldrw_sg_os_uw, MO_UL, uint32_t, ldl, 4, uint32_t, uint32_t, ADDR_ADD_OSW, false) DO_VLDR64_SG(vldrd_sg_os_ud, ADDR_ADD_OSD, false) DO_VSTR_SG(vstrb_sg_ub, MO_UB, stb, 1, uint8_t, ADDR_ADD, false) DO_VSTR_SG(vstrb_sg_uh, MO_UB, stb, 2, uint16_t, ADDR_ADD, false) DO_VSTR_SG(vstrb_sg_uw, MO_UB, stb, 4, uint32_t, ADDR_ADD, false) -DO_VSTR_SG(vstrh_sg_uh, MO_TE | MO_UW, stw, 2, uint16_t, ADDR_ADD, false) -DO_VSTR_SG(vstrh_sg_uw, MO_TE | MO_UW, stw, 4, uint32_t, ADDR_ADD, false) -DO_VSTR_SG(vstrw_sg_uw, MO_TE | MO_UL, stl, 4, uint32_t, ADDR_ADD, false) +DO_VSTR_SG(vstrh_sg_uh, MO_UW, stw, 2, uint16_t, ADDR_ADD, false) +DO_VSTR_SG(vstrh_sg_uw, MO_UW, stw, 4, uint32_t, ADDR_ADD, false) +DO_VSTR_SG(vstrw_sg_uw, MO_UL, stl, 4, uint32_t, ADDR_ADD, false) DO_VSTR64_SG(vstrd_sg_ud, ADDR_ADD, false) -DO_VSTR_SG(vstrh_sg_os_uh, MO_TE | MO_UW, stw, 2, uint16_t, ADDR_ADD_OSH, false) -DO_VSTR_SG(vstrh_sg_os_uw, MO_TE | MO_UW, stw, 4, uint32_t, ADDR_ADD_OSH, false) -DO_VSTR_SG(vstrw_sg_os_uw, MO_TE | MO_UL, stl, 4, uint32_t, ADDR_ADD_OSW, false) +DO_VSTR_SG(vstrh_sg_os_uh, MO_UW, stw, 2, uint16_t, ADDR_ADD_OSH, false) +DO_VSTR_SG(vstrh_sg_os_uw, MO_UW, stw, 4, uint32_t, ADDR_ADD_OSH, false) +DO_VSTR_SG(vstrw_sg_os_uw, MO_UL, stl, 4, uint32_t, ADDR_ADD_OSW, false) DO_VSTR64_SG(vstrd_sg_os_ud, ADDR_ADD_OSD, false) -DO_VLDR_SG(vldrw_sg_wb_uw, MO_TE | MO_UL, uint32_t, ldl, 4, - uint32_t, uint32_t, ADDR_ADD, true) +DO_VLDR_SG(vldrw_sg_wb_uw, MO_UL, uint32_t, ldl, 4, uint32_t, uint32_t, ADDR_ADD, true) DO_VLDR64_SG(vldrd_sg_wb_ud, ADDR_ADD, true) -DO_VSTR_SG(vstrw_sg_wb_uw, MO_TE | MO_UL, stl, 4, uint32_t, ADDR_ADD, true) +DO_VSTR_SG(vstrw_sg_wb_uw, MO_UL, stl, 4, uint32_t, ADDR_ADD, true) DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) /* -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Suggested-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260414005348.4767-6-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/internals.h | 6 ++++++ target/arm/tcg/mve_helper.c | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -XXX,XX +XXX,XX @@ #include "exec/hwaddr.h" #include "exec/vaddr.h" #include "exec/breakpoint.h" +#include "exec/memop.h" #include "accel/tcg/tb-cpu-state.h" #include "hw/core/registerfields.h" #include "tcg/tcg-gvec-desc.h" @@ -XXX,XX +XXX,XX @@ #define BANK_HYP 6 #define BANK_MON 7 +static inline MemOp mo_endian(CPUARMState *env) +{ + return EX_TBFLAG_ANY(env->hflags, BE_DATA) ? MO_BE : MO_LE; +} + static inline int arm_env_mmu_index(CPUARMState *env) { return EX_TBFLAG_ANY(env->hflags, MMUIDX); diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_UW, 2, stw, 4, int32_t) unsigned e; \ uint32_t addr; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (e = 0; e < 16 / 4; e++, mask >>= 4, eci_mask >>= 4) { \ if (!(eci_mask & 1)) { \ continue; \ -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260414005348.4767-7-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/mve_helper.c | 47 +++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -XXX,XX +XXX,XX @@ static void mve_advance_vpt(CPUARMState *env) uint16_t eci_mask = mve_eci_mask(env); \ unsigned b, e; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MFLAG | MO_ALIGN, \ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MFLAG | MO_ALIGN, \ mmu_idx); \ /* \ * R_SXTM allows the dest reg to become UNKNOWN for abandoned \ @@ -XXX,XX +XXX,XX @@ static void mve_advance_vpt(CPUARMState *env) uint16_t mask = mve_element_mask(env); \ unsigned b, e; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MFLAG | MO_ALIGN, \ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MFLAG | MO_ALIGN, \ mmu_idx); \ for (b = 0, e = 0; b < 16; b += ESIZE, e++) { \ if (mask & (1 << b)) { \ @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_UW, 2, stw, 4, int32_t) unsigned e; \ uint32_t addr; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MFLAG | MO_ALIGN, \ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MFLAG | MO_ALIGN, \ mmu_idx); \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE, eci_mask >>= ESIZE) { \ if (!(eci_mask & 1)) { \ @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_UW, 2, stw, 4, int32_t) unsigned e; \ uint32_t addr; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MFLAG | MO_ALIGN, \ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MFLAG | MO_ALIGN, \ mmu_idx); \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE, eci_mask >>= ESIZE) { \ if (!(eci_mask & 1)) { \ @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_UW, 2, stw, 4, int32_t) unsigned e; \ uint32_t addr; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (e = 0; e < 16 / 4; e++, mask >>= 4, eci_mask >>= 4) { \ if (!(eci_mask & 1)) { \ continue; \ @@ -XXX,XX +XXX,XX @@ DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) static const uint8_t off[4] = { O1, O2, O3, O4 }; \ uint32_t addr, data; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) int y; /* y counts 0 2 0 2 */ \ uint16_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0, y = 0; beat < 4; beat++, mask >>= 4, y ^= 2) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) uint32_t *qd; \ int y; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD4W(vld43w, 6, 7, 8, 9) uint32_t addr, data; \ uint8_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD4W(vld43w, 6, 7, 8, 9) int e; \ uint16_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD4W(vld43w, 6, 7, 8, 9) uint32_t addr, data; \ uint32_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD2W(vld21w, 8, 12, 16, 20) static const uint8_t off[4] = { O1, O2, O3, O4 }; \ uint32_t addr, data; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD2W(vld21w, 8, 12, 16, 20) int y; /* y counts 0 2 0 2 */ \ uint16_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0, y = 0; beat < 4; beat++, mask >>= 4, y ^= 2) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD2W(vld21w, 8, 12, 16, 20) uint32_t *qd; \ int y; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VST4W(vst43w, 6, 7, 8, 9) uint32_t addr, data; \ uint8_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VST4W(vst43w, 6, 7, 8, 9) int e; \ uint16_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VST4W(vst43w, 6, 7, 8, 9) uint32_t addr, data; \ uint32_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260414005348.4767-8-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/meson.build | 3 ++- target/arm/tcg/mve_helper.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -XXX,XX +XXX,XX @@ arm_ss.add(when: 'TARGET_AARCH64', if_false: files('stubs32.c')) arm_ss.add(files( 'cpu32.c', 'm_helper.c', - 'mve_helper.c', )) arm_ss.add(when: 'TARGET_AARCH64', if_true: files( @@ -XXX,XX +XXX,XX @@ arm_common_system_ss.add( 'debug.c', 'hflags.c', 'gengvec.c', + 'mve_helper.c', 'neon_helper.c', 'op_helper.c', 'psci.c', @@ -XXX,XX +XXX,XX @@ arm_user_ss.add( 'debug.c', 'gengvec.c', 'hflags.c', + 'mve_helper.c', 'neon_helper.c', 'op_helper.c', 'tlb_helper.c', diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -XXX,XX +XXX,XX @@ #include "helper-mve.h" #include "internals.h" #include "vec_internal.h" -#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/cpu-ldst-common.h" #include "tcg/tcg.h" #include "fpu/softfloat.h" #include "crypto/clmul.h" -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260414005348.4767-9-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/m_helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -XXX,XX +XXX,XX @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) /* Note that these stores can throw exceptions on MPU faults */ ARMMMUIdx mmu_idx = arm_mmu_idx(env); - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, arm_to_core_mmu_idx(mmu_idx)); cpu_stl_mmu(env, sp, nextinst, oi, GETPC()); cpu_stl_mmu(env, sp + 4, saved_psr, oi, GETPC()); @@ -XXX,XX +XXX,XX @@ void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) bool lspact = env->v7m.fpccr[s] & R_V7M_FPCCR_LSPACT_MASK; uintptr_t ra = GETPC(); ARMMMUIdx mmu_idx = arm_mmu_idx(env); - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, arm_to_core_mmu_idx(mmu_idx)); assert(env->v7m.secure); @@ -XXX,XX +XXX,XX @@ void HELPER(v7m_vlldm)(CPUARMState *env, uint32_t fptr) ARMCPU *cpu = env_archcpu(env); uintptr_t ra = GETPC(); ARMMMUIdx mmu_idx = arm_mmu_idx(env); - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, arm_to_core_mmu_idx(mmu_idx)); /* fptr is the value of Rn, the frame pointer we load the FP regs from */ -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260414005348.4767-10-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/m_helper.c | 2 +- target/arm/tcg/meson.build | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -XXX,XX +XXX,XX @@ #include "qemu/log.h" #include "exec/page-protection.h" #ifdef CONFIG_TCG -#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/cpu-ldst-common.h" #include "semihosting/common-semi.h" #endif #if !defined(CONFIG_USER_ONLY) diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -XXX,XX +XXX,XX @@ arm_ss.add(when: 'TARGET_AARCH64', if_false: files('stubs32.c')) arm_ss.add(files( 'cpu32.c', - 'm_helper.c', )) arm_ss.add(when: 'TARGET_AARCH64', if_true: files( @@ -XXX,XX +XXX,XX @@ arm_common_system_ss.add( 'debug.c', 'hflags.c', 'gengvec.c', + 'm_helper.c', 'mve_helper.c', 'neon_helper.c', 'op_helper.c', @@ -XXX,XX +XXX,XX @@ arm_user_ss.add( 'debug.c', 'gengvec.c', 'hflags.c', + 'm_helper.c', 'mve_helper.c', 'neon_helper.c', 'op_helper.c', -- 2.43.0
From: Thomas Huth <thuth@redhat.com> When writing to this register, QEMU currently aborts: $ echo "readl 0x10000018" | ./qemu-system-arm -audiodev none,id=snd0 \ -M integratorcp,accel=qtest,audiodev=snd0 -display none -qtest stdio [I 0.000000] OPENED [R +0.001907] readl 0x10000018 qemu: hardware error: integratorcm_read: CM_LMBUSCNT [...] Aborted (core dumped) This is bad, a guest should ideally never be able to kill QEMU like this. Now, according to the "Intergrator/CP User Guide" from: https://developer.arm.com/documentation/dui0159/b/porting-integrator-ap-and-im-pd1/registers "The Integrator/AP CM_LMBUSCNT has been removed." That means this register does not seem to be implemented on real CP boards at all, only for older AP boards. Thus it should be fine if we simply ignore this register in QEMU and handle it like all other unimplemented registers in the "default" handler of the case statement. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3407 Signed-off-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260420064933.64765-1-thuth@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/integratorcp.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -XXX,XX +XXX,XX @@ static uint64_t integratorcm_read(void *opaque, hwaddr offset, } else { return s->cm_lock; } - case 6: /* CM_LMBUSCNT */ - /* ??? High frequency timer. */ - hw_error("integratorcm_read: CM_LMBUSCNT"); case 7: /* CM_AUXOSC */ return s->cm_auxosc; case 8: /* CM_SDRAM */ -- 2.43.0
include/hw/arm/primecell.h used to be more expansive, but now the only thing it defines is the ARM_SYSCTL_GPIO_* constants for the GPIO lines for the arm-sysctl system-control device used on the Realview, Versatile and Versatile Express boards. Replace it with a header file specific to that device. virt.c and vmapple.c included primecell.h despite not using the constants it defined; there we can simply drop the include entirely. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260416172627.690396-1-peter.maydell@linaro.org --- MAINTAINERS | 2 +- hw/arm/realview.c | 2 +- hw/arm/vexpress.c | 2 +- hw/arm/virt.c | 1 - hw/misc/arm_sysctl.c | 2 +- hw/vmapple/vmapple.c | 1 - include/hw/arm/primecell.h | 12 ------------ include/hw/misc/arm_sysctl.h | 16 ++++++++++++++++ 8 files changed, 20 insertions(+), 18 deletions(-) delete mode 100644 include/hw/arm/primecell.h create mode 100644 include/hw/misc/arm_sysctl.h diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ F: hw/ssi/pl022.c F: include/hw/ssi/pl022.h F: hw/rtc/pl031.c F: include/hw/rtc/pl031.h -F: include/hw/arm/primecell.h F: hw/timer/cmsdk-apb-timer.c F: include/hw/timer/cmsdk-apb-timer.h F: tests/qtest/cmsdk-apb-timer-test.c @@ -XXX,XX +XXX,XX @@ F: hw/*/versatile* F: hw/i2c/arm_sbcon_i2c.c F: include/hw/i2c/arm_sbcon_i2c.h F: hw/misc/arm_sysctl.c +F: include/hw/misc/arm_sysctl.h F: docs/system/arm/versatile.rst Virt diff --git a/hw/arm/realview.c b/hw/arm/realview.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -XXX,XX +XXX,XX @@ #include "target/arm/cpu.h" #include "hw/core/sysbus.h" #include "hw/arm/boot.h" -#include "hw/arm/primecell.h" #include "hw/arm/machines-qom.h" #include "hw/core/split-irq.h" +#include "hw/misc/arm_sysctl.h" #include "hw/net/lan9118.h" #include "hw/net/smc91c111.h" #include "hw/pci/pci.h" diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -XXX,XX +XXX,XX @@ #include "qemu/datadir.h" #include "hw/core/sysbus.h" #include "hw/arm/boot.h" -#include "hw/arm/primecell.h" #include "hw/arm/machines-qom.h" +#include "hw/misc/arm_sysctl.h" #include "hw/net/lan9118.h" #include "hw/i2c/i2c.h" #include "net/net.h" diff --git a/hw/arm/virt.c b/hw/arm/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -XXX,XX +XXX,XX @@ #include "monitor/qdev.h" #include "hw/core/sysbus.h" #include "hw/arm/boot.h" -#include "hw/arm/primecell.h" #include "hw/arm/virt.h" #include "hw/arm/machines-qom.h" #include "hw/block/flash.h" diff --git a/hw/misc/arm_sysctl.c b/hw/misc/arm_sysctl.c index XXXXXXX..XXXXXXX 100644 --- a/hw/misc/arm_sysctl.c +++ b/hw/misc/arm_sysctl.c @@ -XXX,XX +XXX,XX @@ #include "system/runstate.h" #include "qemu/bitops.h" #include "hw/core/sysbus.h" +#include "hw/misc/arm_sysctl.h" #include "migration/vmstate.h" -#include "hw/arm/primecell.h" #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" diff --git a/hw/vmapple/vmapple.c b/hw/vmapple/vmapple.c index XXXXXXX..XXXXXXX 100644 --- a/hw/vmapple/vmapple.c +++ b/hw/vmapple/vmapple.c @@ -XXX,XX +XXX,XX @@ #include "hw/core/sysbus.h" #include "hw/usb/usb.h" #include "hw/arm/boot.h" -#include "hw/arm/primecell.h" #include "hw/char/pl011.h" #include "hw/intc/arm_gic.h" #include "hw/intc/arm_gicv3_common.h" diff --git a/include/hw/arm/primecell.h b/include/hw/arm/primecell.h deleted file mode 100644 index XXXXXXX..XXXXXXX --- a/include/hw/arm/primecell.h +++ /dev/null @@ -XXX,XX +XXX,XX @@ -#ifndef PRIMECELL_H -#define PRIMECELL_H - -/* Declarations for ARM PrimeCell based periperals. */ -/* Also includes some devices that are currently only used by the - ARM boards. */ - -/* arm_sysctl GPIO lines */ -#define ARM_SYSCTL_GPIO_MMC_WPROT 0 -#define ARM_SYSCTL_GPIO_MMC_CARDIN 1 - -#endif diff --git a/include/hw/misc/arm_sysctl.h b/include/hw/misc/arm_sysctl.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/include/hw/misc/arm_sysctl.h @@ -XXX,XX +XXX,XX @@ +/* + * Status and system control registers for ARM RealView/Versatile boards. + * + * Copyright (c) Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_MISC_ARM_SYSCTL_H +#define HW_MISC_ARM_SYSCTL_H + +/* arm_sysctl inbound GPIO lines */ +#define ARM_SYSCTL_GPIO_MMC_WPROT 0 +#define ARM_SYSCTL_GPIO_MMC_CARDIN 1 + +#endif -- 2.43.0
If we create a normally-AArch64 CPU and configure it with aarch64=off, this will by default leave all the AArch64 ID register values in its ARMISARegisters struct untouched. That in turn means that tests of cpu_isar_feature(aa64_something, cpu) will return true. Until now we have had a design policy that you shouldn't check an aa64_ feature unless you know that the CPU has AArch64; but this is quite fragile as it's easy to forget and only causes a problem in the corner case where AArch64 was turned off. In particular, when we extend the ability to disable AArch64 from only KVM to also TCG there are many more aa64 feature check points which we would otherwise have to audit for whether they needed to be guarded with a check on ARM_FEATURE_AARCH64. Instead, make the CPU realize function zero out all the 64-bit ID registers if a TCG CPU doesn't have AArch64; this will make aa64_ feature tests generally return false. We only do this for TCG because only TCG really needs it, and for KVM it might be confusing to have QEMU's idea of the ID registers be different from KVM's. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260416165353.589569-2-peter.maydell@linaro.org --- target/arm/cpu.c | 35 +++++++++++++++++++++++++++++++++++ target/arm/cpu.h | 3 ++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -XXX,XX +XXX,XX @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp) } } +static void arm_clear_aarch64_idregs(ARMCPU *cpu) +{ + /* Zero out all the AArch64 ID registers in ARMISARegisters */ + SET_IDREG(&cpu->isar, ID_AA64ISAR0, 0); + SET_IDREG(&cpu->isar, ID_AA64ISAR1, 0); + SET_IDREG(&cpu->isar, ID_AA64ISAR2, 0); + SET_IDREG(&cpu->isar, ID_AA64PFR0, 0); + SET_IDREG(&cpu->isar, ID_AA64PFR1, 0); + SET_IDREG(&cpu->isar, ID_AA64PFR2, 0); + SET_IDREG(&cpu->isar, ID_AA64MMFR0, 0); + SET_IDREG(&cpu->isar, ID_AA64MMFR1, 0); + SET_IDREG(&cpu->isar, ID_AA64MMFR2, 0); + SET_IDREG(&cpu->isar, ID_AA64MMFR3, 0); + SET_IDREG(&cpu->isar, ID_AA64DFR0, 0); + SET_IDREG(&cpu->isar, ID_AA64DFR1, 0); + SET_IDREG(&cpu->isar, ID_AA64AFR0, 0); + SET_IDREG(&cpu->isar, ID_AA64AFR1, 0); + SET_IDREG(&cpu->isar, ID_AA64ZFR0, 0); + SET_IDREG(&cpu->isar, ID_AA64SMFR0, 0); +} + static void arm_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); @@ -XXX,XX +XXX,XX @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } #endif + /* + * A TCG aarch64=off CPU has no AArch64 at all, so we clear out the + * ID registers to avoid cpu_isar_feature(aa64_something, cpu) tests + * incorrectly returning true. We don't do this for other accelerators + * (which in practice means "for KVM", since no others have AArch32 + * guest support) because from KVM's point of view the AArch64 ID + * registers still exist and must have their correct values. So we + * avoid clearing them out so that we don't have QEMU and KVM with + * different ideas of the ID registers. + */ + if (tcg_enabled() && !arm_feature(env, ARM_FEATURE_AARCH64)) { + arm_clear_aarch64_idregs(cpu); + } + #ifdef CONFIG_USER_ONLY /* * User mode relies on IC IVAU instructions to catch modification of diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ struct ArchCPU { * Note that if you add an ID register to the ARMISARegisters struct * you need to also update the 32-bit and 64-bit versions of the * kvm_arm_get_host_cpu_features() function to correctly populate the - * field by reading the value from the KVM vCPU. + * field by reading the value from the KVM vCPU. If it is an AArch64 + * ID register then you also must update arm_clear_aarch64_idregs(). */ struct ARMISARegisters { uint32_t mvfr0; -- 2.43.0
Allow the 'aarch64=off' property, which is currently KVM-only, to be set for TCG CPUs also. Note that we don't permit it on the qemu-aarch64 user-mode binary: this makes no sense as that executable can only handle AArch64 syscalls (and it would also assert at startup since it doesn't compile in the A32-specific GDB xml files like arm-neon.xml). Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Clément Chigot <chigot@adacore.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260416165353.589569-3-peter.maydell@linaro.org --- docs/system/arm/cpu-features.rst | 10 +++++---- target/arm/cpu-features.h | 5 +++++ target/arm/cpu.c | 36 ++++++++++++++++++++++++++++---- tests/qtest/arm-cpu-features.c | 8 ++----- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/docs/system/arm/cpu-features.rst b/docs/system/arm/cpu-features.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/arm/cpu-features.rst +++ b/docs/system/arm/cpu-features.rst @@ -XXX,XX +XXX,XX @@ not implement ARMv8-A, will not have the ``aarch64`` CPU property. QEMU's support may be limited for some CPU features, only partially supporting the feature or only supporting the feature under certain configurations. For example, the ``aarch64`` CPU feature, which, when -disabled, enables the optional AArch32 CPU feature, is only supported -when using the KVM accelerator and when running on a host CPU type that -supports the feature. While ``aarch64`` currently only works with KVM, -it could work with TCG. CPU features that are specific to KVM are +disabled, enables the optional AArch32 CPU feature, can only be set to +``off`` on the TCG and KVM accelerators, and it cannot be set to +``off`` under KVM unless running on a host CPU type that supports +running guests in AArch32. + +CPU features that are inherently specific to KVM are prefixed with "kvm-" and are described in "KVM VCPU Features". CPU Feature Probing diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -XXX,XX +XXX,XX @@ static inline bool isar_feature_aa64_aa32_el2(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64PFR0, EL2) >= 2; } +static inline bool isar_feature_aa64_aa32_el3(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64PFR0, EL3) >= 2; +} + static inline bool isar_feature_aa64_ras(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64PFR0, RAS) != 0; diff --git a/target/arm/cpu.c b/target/arm/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -XXX,XX +XXX,XX @@ static void aarch64_cpu_set_aarch64(Object *obj, bool value, Error **errp) * uniform execution state like do_interrupt. */ if (value == false) { - if (!kvm_enabled() || !kvm_arm_aarch32_supported()) { - error_setg(errp, "'aarch64' feature cannot be disabled " - "unless KVM is enabled and 32-bit EL1 " - "is supported"); + if (kvm_enabled()) { + if (!kvm_arm_aarch32_supported()) { + error_setg(errp, "'aarch64' feature cannot be disabled for KVM " + "because this host does not support 32-bit EL1"); + return; + } + } else if (tcg_enabled()) { +#ifdef CONFIG_USER_ONLY + error_setg(errp, "'aarch64' feature cannot be disabled for " + "usermode emulator qemu-aarch64; use qemu-arm instead"); + return; +#else + bool aa32_at_highest_el; + if (arm_feature(&cpu->env, ARM_FEATURE_EL3)) { + aa32_at_highest_el = cpu_isar_feature(aa64_aa32_el3, cpu); + } else if (arm_feature(&cpu->env, ARM_FEATURE_EL2)) { + aa32_at_highest_el = cpu_isar_feature(aa64_aa32_el2, cpu); + } else { + aa32_at_highest_el = cpu_isar_feature(aa64_aa32_el1, cpu); + } + + if (!aa32_at_highest_el) { + error_setg(errp, "'aarch64' feature cannot be disabled for " + "this TCG CPU because it does not support 32-bit " + "execution at its highest implemented exception " + "level"); + return; + } +#endif + } else { + error_setg(errp, "'aarch64' feature cannot be disabled for " + "this accelerator"); return; } unset_feature(&cpu->env, ARM_FEATURE_AARCH64); diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/arm-cpu-features.c +++ b/tests/qtest/arm-cpu-features.c @@ -XXX,XX +XXX,XX @@ static void test_query_cpu_model_expansion(const void *data) sve_tests_default(qts, "max"); pauth_tests_default(qts, "max"); - /* Test that features that depend on KVM generate errors without. */ - assert_error(qts, "max", - "'aarch64' feature cannot be disabled " - "unless KVM is enabled and 32-bit EL1 " - "is supported", - "{ 'aarch64': false }"); + /* TCG allows us to turn off AArch64 on the 'max' CPU type */ + assert_set_feature(qts, "max", "aarch64", false); } qtest_quit(qts); -- 2.43.0
Add a basic test of the TCG 'aarch64=off' functionality; this is the same as our existing arm/test_virt test, but it runs the AArch32 guest kernel on qemu-system-aarch64 with -cpu max,aarch64=off. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260416165353.589569-4-peter.maydell@linaro.org --- tests/functional/aarch64/meson.build | 1 + .../aarch64/test_virt_aarch64_off.py | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100755 tests/functional/aarch64/test_virt_aarch64_off.py diff --git a/tests/functional/aarch64/meson.build b/tests/functional/aarch64/meson.build index XXXXXXX..XXXXXXX 100644 --- a/tests/functional/aarch64/meson.build +++ b/tests/functional/aarch64/meson.build @@ -XXX,XX +XXX,XX @@ tests_aarch64_system_thorough = [ 'tcg_plugins', 'tuxrun', 'virt', + 'virt_aarch64_off', 'virt_gpu', 'virt_vbsa', 'xen', diff --git a/tests/functional/aarch64/test_virt_aarch64_off.py b/tests/functional/aarch64/test_virt_aarch64_off.py new file mode 100755 index XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/functional/aarch64/test_virt_aarch64_off.py @@ -XXX,XX +XXX,XX @@ +#!/usr/bin/env python3 +# +# Functional test that boots an AArch32 Linux kernel and checks the console +# on a TCG aarch64 CPU with aarch64=off. This is the same image etc +# as we use in tests/functional/arm/test_virt.py. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + +class ArmVirtMachine(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/29/Everything/armhfp/os/images/pxeboot/vmlinuz'), + '18dd5f1a9a28bd539f9d047f7c0677211bae528e8712b40ca5a229a4ad8e2591') + + def test_arm_virt(self): + self.set_machine('virt') + # KVM aarch64=off requires a host CPU that supports it, so + # restrict the test to TCG only + self.require_accelerator('tcg') + self.vm.add_args('-cpu', 'max,aarch64=off') + + kernel_path = self.ASSET_KERNEL.fetch() + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + +if __name__ == '__main__': + LinuxKernelTest.main() -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> We introduce a datatype for a tolerance with respect to a given cpreg migration issue. The tolerance applies to a given cpreg kvm index, and can be of different types: a) mismatch in cpreg indexes - ToleranceNotOnBothEnds (cpreg index is allowed to be only present on one end) - ToleranceOnlySrcTestValue (cpreg index is allowed to be only present in source if its value @mask field matches @value) b) mismatch in cpreg values - ToleranceDiffInMask (value differences are allowed only within a mask) - ToleranceFieldLT (incoming field value must be less than a given value) - ToleranceFieldGT (incoming field value must be greater than a given value) A QLIST of such tolerances can be populated using a new helper: arm_register_cpreg_mig_tolerance() and arm_cpu_match_cpreg_mig_tolerance() allows to check whether a tolerance exists for a given kvm index and its criterion is matched. callers for those helpers will be introduced in subsequent patches. Only registration of migration tolerances related to cpreg index mismatch is currently allowed. Signed-off-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260420140552.104369-2-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpu.c | 82 ++++++++++++++++++++++++++++++++++++++++++ target/arm/cpu.h | 1 + target/arm/internals.h | 54 ++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -XXX,XX +XXX,XX @@ void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, QLIST_INSERT_HEAD(&cpu->el_change_hooks, entry, node); } +static ARMCPRegMigTolerance *find_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx) +{ + ARMCPRegMigTolerance *t; + QLIST_FOREACH(t, &cpu->cpreg_mig_tolerances, node) { + if (t->kvmidx == kvmidx) { + return t; + } + } + return NULL; +} + +void arm_register_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx, + uint64_t mask, uint64_t value, + ARMCPRegMigToleranceType type) +{ + ARMCPRegMigTolerance *entry; + + /* make sure the kvmidx has not tolerance already registered */ + assert(!find_mig_tolerance(cpu, kvmidx)); + + assert(type == ToleranceNotOnBothEnds || + type == ToleranceOnlySrcTestValue); + + entry = g_new0(ARMCPRegMigTolerance, 1); + + entry->kvmidx = kvmidx; + entry->mask = mask; + entry->value = value; + entry->type = type; + + QLIST_INSERT_HEAD(&cpu->cpreg_mig_tolerances, entry, node); +} + +bool arm_cpu_match_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx, + uint64_t vmstate_value, uint64_t local_value, + ARMCPRegMigToleranceType type) +{ + ARMCPRegMigTolerance *t = find_mig_tolerance(cpu, kvmidx); + uint64_t diff, diff_outside_mask, field; + + if (!t || t->type != type) { + return false; + } + + if (type == ToleranceNotOnBothEnds) { + return true; + } + + if (type == ToleranceOnlySrcTestValue && + ((vmstate_value & t->mask) == t->value)) { + return true; + } + + /* Need to check the mask */ + diff = vmstate_value ^ local_value; + diff_outside_mask = diff & ~t->mask; + + if (diff_outside_mask) { + /* there are differences outside of the mask */ + return false; + } + if (type == ToleranceDiffInMask) { + /* differences only in the field, tolerance matched */ + return true; + } + /* need to compare field value against authorized ones */ + field = vmstate_value & t->mask; + if (type == ToleranceFieldLT && (field < t->value)) { + return true; + } + if (type == ToleranceFieldGT && (field > t->value)) { + return true; + } + return false; +} + static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) { /* Reset a single ARMCPRegInfo register */ @@ -XXX,XX +XXX,XX @@ static void arm_cpu_initfn(Object *obj) QLIST_INIT(&cpu->pre_el_change_hooks); QLIST_INIT(&cpu->el_change_hooks); + QLIST_INIT(&cpu->cpreg_mig_tolerances); #ifdef CONFIG_USER_ONLY # ifdef TARGET_AARCH64 @@ -XXX,XX +XXX,XX @@ static void arm_cpu_finalizefn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); ARMELChangeHook *hook, *next; + ARMCPRegMigTolerance *t, *n; g_hash_table_destroy(cpu->cp_regs); @@ -XXX,XX +XXX,XX @@ static void arm_cpu_finalizefn(Object *obj) QLIST_REMOVE(hook, node); g_free(hook); } + QLIST_FOREACH_SAFE(t, &cpu->cpreg_mig_tolerances, node, n) { + QLIST_REMOVE(t, node); + g_free(t); + } #ifndef CONFIG_USER_ONLY if (cpu->pmu_timer) { timer_free(cpu->pmu_timer); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ struct ArchCPU { QLIST_HEAD(, ARMELChangeHook) pre_el_change_hooks; QLIST_HEAD(, ARMELChangeHook) el_change_hooks; + QLIST_HEAD(, ARMCPRegMigTolerance) cpreg_mig_tolerances; int32_t node_id; /* NUMA node this CPU belongs to */ diff --git a/target/arm/internals.h b/target/arm/internals.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -XXX,XX +XXX,XX @@ int compare_u64(const void *a, const void *b); /* Used in FEAT_MEC to set the MECIDWidthm1 field in the MECIDR_EL2 register. */ #define MECID_WIDTH 16 +typedef enum { + ToleranceNotOnBothEnds, + ToleranceOnlySrcTestValue, + ToleranceDiffInMask, + ToleranceFieldLT, + ToleranceFieldGT, +} ARMCPRegMigToleranceType; + +typedef struct ARMCPRegMigTolerance { + uint64_t kvmidx; + uint64_t mask; + uint64_t value; + ARMCPRegMigToleranceType type; + QLIST_ENTRY(ARMCPRegMigTolerance) node; +} ARMCPRegMigTolerance; + +/** + * arm_register_cpreg_mig_tolerance: + * Register a migration tolerance wrt one given cpreg identified by its + * @kvmidx. Calling this function twice for the same @kvmidx is a + * programming error and will cause an assertion failure. + * + * @cpu: vcpu to apply the migration tolerance on + * @kvmidx: kvm index of the cpreg the tolerance applies to + * @mask: bitmask where a difference is tolerated + * (relevant with ToleranceDiffInMask) + * @value: value the bitmask field is compared with + * (relevant with ToleranceFieldLT and ToleranceFieldGT) + * @type: type of the migration tolerance: + * - ToleranceNotOnBothEnds (cpreg index is allowed to be only present + * on one end) + * - ToleranceOnlySrcTestValue (cpreg index is allowed to be only + * present in source if its value @mask field matches @value) + * - ToleranceDiffInMask (mismatch in cpreg values are only tolerated + * if differences are within @mask) + * - ToleranceFieldLT (mismatch in cpreg values are only tolerated + * if incoming @bitmask field value is less than @value) + * - ToleranceFieldGT (mismatch in cpreg values are only tolerated + * if incoming @bitmask field value is greater than @value) + */ +void arm_register_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx, + uint64_t mask, uint64_t value, + ARMCPRegMigToleranceType type); + +/** + * arm_cpu_match_cpreg_mig_tolerance: + * Check whether a tolerance of type @type exists for a given @kvmidx + * and the tolerance criterion is satisfied + */ +bool arm_cpu_match_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx, + uint64_t vmstate_value, uint64_t local_value, + ARMCPRegMigToleranceType type); + + #endif -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> If there is a mismatch between the cpreg indexes found on both ends, check whether a tolerance was registered for the given kvmidx. If any, silence warning/errors. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260420140552.104369-3-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/machine.c | 21 +++++++++++++++------ target/arm/trace-events | 2 ++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/target/arm/machine.c b/target/arm/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -XXX,XX +XXX,XX @@ static void handle_cpreg_missing_in_incoming_stream(ARMCPU *cpu, uint64_t kvmidx { g_autofree gchar *name = print_register_name(kvmidx); + if (arm_cpu_match_cpreg_mig_tolerance(cpu, kvmidx, + 0, 0, ToleranceNotOnBothEnds)) { + trace_tolerate_cpreg_missing_in_incoming_stream(name); + return; + } warn_report("%s: %s " "expected by the destination but not in the incoming stream: " "skip it", __func__, name); } /* - * Handle the situation where @kvmidx is in the incoming stream - * but not on destination. This currently fails the migration but - * we plan to accomodate some exceptions, hence the boolean returned value. + * Handle the situation where @kvmidx is in the incoming + * stream but not on destination. This fails the migration if + * no cpreg mig tolerance is matched for this @kvmidx + * Return true if the migration should eventually fail */ static bool handle_cpreg_only_in_incoming_stream(ARMCPU *cpu, uint64_t kvmidx) { g_autofree gchar *name = print_register_name(kvmidx); - bool fail = true; + if (arm_cpu_match_cpreg_mig_tolerance(cpu, kvmidx, + 0, 0, ToleranceNotOnBothEnds)) { + trace_tolerate_cpreg_only_in_incoming_stream(name); + return false; + } error_report("%s: %s in the incoming stream but unknown on the " "destination: fail migration", __func__, name); - - return fail; + return true; } static int cpu_post_load(void *opaque, int version_id) diff --git a/target/arm/trace-events b/target/arm/trace-events index XXXXXXX..XXXXXXX 100644 --- a/target/arm/trace-events +++ b/target/arm/trace-events @@ -XXX,XX +XXX,XX @@ arm_psci_call(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint32_t cpuid # machine.c cpu_post_load(uint32_t cpreg_vmstate_array_len, uint32_t cpreg_array_len) "cpreg_vmstate_array_len=%d cpreg_array_len=%d" +tolerate_cpreg_missing_in_incoming_stream(char *name) "%s is missing in incoming stream but this is explicitly tolerated" +tolerate_cpreg_only_in_incoming_stream(char *name) "%s is in incoming stream but not on destination but this is explicitly tolerated" -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> Pass the value of the incoming register to handle_cpreg_only_in_incoming_stream and check whether there is a matching ToleranceOnlySrcTestValue tolerance. Signed-off-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260420140552.104369-4-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/machine.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/target/arm/machine.c b/target/arm/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -XXX,XX +XXX,XX @@ static void handle_cpreg_missing_in_incoming_stream(ARMCPU *cpu, uint64_t kvmidx * no cpreg mig tolerance is matched for this @kvmidx * Return true if the migration should eventually fail */ -static bool handle_cpreg_only_in_incoming_stream(ARMCPU *cpu, uint64_t kvmidx) +static bool +handle_cpreg_only_in_incoming_stream(ARMCPU *cpu, uint64_t kvmidx, uint64_t value) { g_autofree gchar *name = print_register_name(kvmidx); if (arm_cpu_match_cpreg_mig_tolerance(cpu, kvmidx, - 0, 0, ToleranceNotOnBothEnds)) { + 0, 0, ToleranceNotOnBothEnds) || + arm_cpu_match_cpreg_mig_tolerance(cpu, kvmidx, + value, 0, ToleranceOnlySrcTestValue)) { trace_tolerate_cpreg_only_in_incoming_stream(name); return false; } @@ -XXX,XX +XXX,XX @@ static int cpu_post_load(void *opaque, int version_id) } if (cpu->cpreg_vmstate_indexes[v] < cpu->cpreg_indexes[i]) { fail = handle_cpreg_only_in_incoming_stream(cpu, - cpu->cpreg_vmstate_indexes[v++]); + cpu->cpreg_vmstate_indexes[v], + cpu->cpreg_vmstate_values[v]); + v++; continue; } /* matching register, copy the value over */ @@ -XXX,XX +XXX,XX @@ static int cpu_post_load(void *opaque, int version_id) */ for ( ; v < cpu->cpreg_vmstate_array_len; v++) { fail = handle_cpreg_only_in_incoming_stream(cpu, - cpu->cpreg_vmstate_indexes[v]); + cpu->cpreg_vmstate_indexes[v], + cpu->cpreg_vmstate_values[v]); } if (fail) { return -1; -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> Before linux v6.13 those registers were erroneously unconditionally exposed and this was fixed by commits: - 0fcb4eea5345 ("KVM: arm64: Hide TCR2_EL1 from userspace when disabled for guests") - a68cddbe47ef ("KVM: arm64: Hide S1PIE registers from userspace when disabled for guests") in v6.13. This means if we migrate from an old kernel host to a >= 6.13 kernel host, migration currently fails. Declare cpreg migration tolerance for those registers. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Sebastian Ott <sebott@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260420140552.104369-5-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpu64.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -XXX,XX +XXX,XX @@ static void aarch64_a53_initfn(Object *obj) define_cortex_a72_a57_a53_cp_reginfo(cpu); } +#if defined(CONFIG_KVM) +static void kvm_arm_set_cpreg_mig_tolerances(ARMCPU *cpu) +{ + /* + * Registers that may be in the incoming stream and not exposed + * on the destination + */ + + /* + * TCR_EL1 was erroneously unconditionnally exposed before linux v6.13. + * See commit 0fcb4eea5345 ("KVM: arm64: Hide TCR2_EL1 from userspace + * when disabled for guests") + */ + arm_register_cpreg_mig_tolerance(cpu, ARM64_SYS_REG(3, 0, 2, 0, 3), + 0, 0, ToleranceNotOnBothEnds); + /* + * PIRE0_EL1 and PIR_EL1 were erroneously unconditionnally exposed + * before linux v6.13. See commit a68cddbe47ef ("KVM: arm64: Hide + * S1PIE registers from userspace when disabled for guests") + */ + arm_register_cpreg_mig_tolerance(cpu, ARM64_SYS_REG(3, 0, 10, 2, 2), + 0, 0, ToleranceNotOnBothEnds); + arm_register_cpreg_mig_tolerance(cpu, ARM64_SYS_REG(3, 0, 10, 2, 3), + 0, 0, ToleranceNotOnBothEnds); +} +#endif + static void aarch64_host_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -XXX,XX +XXX,XX @@ static void aarch64_host_initfn(Object *obj) #endif #if defined(CONFIG_KVM) + kvm_arm_set_cpreg_mig_tolerances(cpu); kvm_arm_set_cpu_features_from_host(cpu); aarch64_add_sve_properties(obj); #elif defined(CONFIG_HVF) -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> KVM_REG_ARM_VENDOR_HYP_BMAP_2 pseudo FW register is exposed from v6.15 onwards. Backward migration from a >= v6.15 to an older kernel would fail without cpreg migration tolerance definition for this register. If the register is present on source but not on destination, its value must be checked to make sure it matches the reset value, ie. 0, meaning no service is exposed to the guest, hence the choice of a ToleranceOnlySrcTestValue migration tolerance. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260420140552.104369-6-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpu64.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -XXX,XX +XXX,XX @@ static void kvm_arm_set_cpreg_mig_tolerances(ARMCPU *cpu) 0, 0, ToleranceNotOnBothEnds); arm_register_cpreg_mig_tolerance(cpu, ARM64_SYS_REG(3, 0, 10, 2, 3), 0, 0, ToleranceNotOnBothEnds); + + /* + * KVM_REG_ARM_VENDOR_HYP_BMAP_2 pseudo FW register is exposed + * from v6.15 onwards. Backward migration from a >= v6.15 to an older + * kernel would fail without cpreg migration tolerance definition. + * If the register is present on source but not on destination, make + * sure it has its reset value, ie. 0, meaning no service is exposed + * to the guest. + */ + arm_register_cpreg_mig_tolerance(cpu, KVM_REG_ARM_FW_FEAT_BMAP_REG(3), + UINT64_MAX, 0, ToleranceOnlySrcTestValue); } #endif -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> We want to remove AArch32 DBGDTRTX which was erroneously exposed. This was attempted by 655659a74a36b ("target/arm: Correct encoding of Debug Communications Channel registers") but it was discovered that the removal of this debug register broke forward migration on TCG. Now we have the cpreg migration tolerance infrastructure, we can declare one for the DBGDTRTX. This allow to revert the reinstate patch. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Sebastian Ott <sebott@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260420140552.104369-7-eric.auger@redhat.com [PMM: revised comment, included note about when we can drop the workaround] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/helper.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .fgt = FGT_CLIDR_EL1, .resetvalue = GET_IDREG(isar, CLIDR) }; + uint64_t dbgtr_el0_kvmidx = + cpreg_to_kvm_id(ENCODE_CP_REG(14, 0, 1, 0, 5, 3, 0)); + define_one_arm_cp_reg(cpu, &clidr); define_arm_cp_regs(cpu, v7_cp_reginfo); define_debug_regs(cpu); + /* + * We used to incorrectly expose a non-existent AArch32 "DBGDTRTX" + * register with this encoding. This has been fixed by commit + * 655659a74a36 ("target/arm: Correct encoding of Debug + * Communications Channel registers") by the introduction of correct + * separate cpreg definitions for AA64 and AA32 versions. However, + * the old cpreg definition couldn't be removed without breaking + * migration, so commit 4f2b82f604 reinstated the bogus encoding + * for migration data only. + * + * Now that we have migration tolerance infrastructure, we can use + * this to allow forward migration from the buggy QEMU versions, + * accepting and ignoring the bogus register if it is in the + * source data. QEMU 11.0 was the last version that sent the + * bogus encoding, so this workaround can be removed at the point + * where we no longer care about migration from that version + * (i.e. when we remove the "virt-11.0" machine type). + */ + arm_register_cpreg_mig_tolerance(cpu, dbgtr_el0_kvmidx, + 0, 0, ToleranceNotOnBothEnds); } else { define_arm_cp_regs(cpu, not_v7_cp_reginfo); } -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> This reverts commit 4f2b82f60431 ("target/arm: Reinstate bogus AArch32 DBGDTRTX register for migration compat). We don't need that commit anymore as the AArch32 DBGDTRTX register is declared to be safe to ignore in the incoming migration stream. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Sebastian Ott <sebott@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260420140552.104369-8-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/debug_helper.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -XXX,XX +XXX,XX @@ static void dbgclaimclr_write(CPUARMState *env, const ARMCPRegInfo *ri, env->cp15.dbgclaim &= ~(value & 0xFF); } -static CPAccessResult access_bogus(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - /* Always UNDEF, as if this cpreg didn't exist */ - return CP_ACCESS_UNDEFINED; -} - static const ARMCPRegInfo debug_cp_reginfo[] = { /* * DBGDRAR, DBGDSAR: always RAZ since we don't implement memory mapped @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo debug_cp_reginfo[] = { .opc0 = 2, .opc1 = 3, .crn = 0, .crm = 4, .opc2 = 0, .access = PL0_RW, .accessfn = access_tdcc, .type = ARM_CP_CONST, .resetvalue = 0 }, - /* - * This is not a real AArch32 register. We used to incorrectly expose - * this due to a QEMU bug; to avoid breaking migration compatibility we - * need to continue to provide it so that we don't fail the inbound - * migration when it tells us about a sysreg that we don't have. - * We set an always-fails .accessfn, which means that the guest doesn't - * actually see this register (it will always UNDEF, identically to if - * there were no cpreg definition for it other than that we won't print - * a LOG_UNIMP message about it), and we set the ARM_CP_NO_GDB flag so the - * gdbstub won't see it either. - * (We can't just set .access = 0, because add_cpreg_to_hashtable() - * helpfully ignores cpregs which aren't accessible to the highest - * implemented EL.) - * - * TODO: implement a system for being able to describe "this register - * can be ignored if it appears in the inbound stream"; then we can - * remove this temporary hack. - */ - { .name = "BOGUS_DBGDTR_EL0", .state = ARM_CP_STATE_AA32, - .cp = 14, .opc1 = 3, .crn = 0, .crm = 5, .opc2 = 0, - .access = PL0_RW, .accessfn = access_bogus, - .type = ARM_CP_CONST | ARM_CP_NO_GDB, .resetvalue = 0 }, /* * OSECCR_EL1 provides a mechanism for an operating system * to access the contents of EDECCR. EDECCR is not implemented though, -- 2.43.0
From: Osama Abdelkader <osama.abdelkader@gmail.com> fdt_node_offset_by_compatible(fdt, -1, compat) only finds the first match. If the blob has more than one node with the same compatible string, extra nodes will remain active. Remove all the matching nodes, using the same loop as imx8mp-evk.c does for this purpose. Signed-off-by: Osama Abdelkader <osama.abdelkader@gmail.com> Message-id: 20260420162114.308519-1-osama.abdelkader@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/raspi4b.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/arm/raspi4b.c b/hw/arm/raspi4b.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/raspi4b.c +++ b/hw/arm/raspi4b.c @@ -XXX,XX +XXX,XX @@ static void raspi4_modify_dtb(const struct arm_boot_info *info, void *fdt) for (int i = 0; i < ARRAY_SIZE(nodes_to_remove); i++) { const char *dev_str = nodes_to_remove[i]; + int offset; - int offset = fdt_node_offset_by_compatible(fdt, -1, dev_str); - if (offset >= 0) { - if (!fdt_nop_node(fdt, offset)) { - warn_report("bcm2711 dtc: %s has been disabled!", dev_str); + offset = fdt_node_offset_by_compatible(fdt, -1, dev_str); + while (offset >= 0) { + if (fdt_nop_node(fdt, offset) == 0) { + warn_report("bcm2711 dtb: %s has been disabled!", dev_str); } + offset = fdt_node_offset_by_compatible(fdt, offset, dev_str); } } -- 2.43.0
From: Yucai Liu <1486344514@qq.com> Implement a basic i.MX6UL LCDIF controller model with MMIO registers, frame-done interrupt behavior, and framebuffer-backed display updates for RGB565 and XRGB8888 input formats. Place the LCDIF device under hw/display and build it via a dedicated CONFIG_IMX6UL_LCDIF symbol. Model register fields with registerfields.h helpers and provide migration support via vmstate. Signed-off-by: Yucai Liu <1486344514@qq.com> Message-id: 20260412110240.93116-2-yangyanglan718@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- MAINTAINERS | 2 + hw/display/Kconfig | 4 + hw/display/imx6ul_lcdif.c | 453 ++++++++++++++++++++++++++++++ hw/display/meson.build | 1 + include/hw/display/imx6ul_lcdif.h | 37 +++ 5 files changed, 497 insertions(+) create mode 100644 hw/display/imx6ul_lcdif.c create mode 100644 include/hw/display/imx6ul_lcdif.h diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ L: qemu-arm@nongnu.org S: Odd Fixes F: hw/arm/mcimx6ul-evk.c F: hw/arm/fsl-imx6ul.c +F: hw/display/imx6ul_lcdif.c F: hw/misc/imx6ul_ccm.c F: include/hw/arm/fsl-imx6ul.h +F: include/hw/display/imx6ul_lcdif.h F: include/hw/misc/imx6ul_ccm.h F: docs/system/arm/mcimx6ul-evk.rst diff --git a/hw/display/Kconfig b/hw/display/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -XXX,XX +XXX,XX @@ config PL110 bool select FRAMEBUFFER +config IMX6UL_LCDIF + bool + select FRAMEBUFFER + config SII9022 bool depends on I2C diff --git a/hw/display/imx6ul_lcdif.c b/hw/display/imx6ul_lcdif.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/display/imx6ul_lcdif.c @@ -XXX,XX +XXX,XX @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * i.MX6UL LCDIF controller + * + * Copyright (c) 2026 Yucai Liu <1486344514@qq.com> + */ + +#include "qemu/osdep.h" +#include "hw/display/imx6ul_lcdif.h" +#include "hw/core/irq.h" +#include "hw/core/registerfields.h" +#include "hw/display/framebuffer.h" +#include "migration/vmstate.h" +#include "system/address-spaces.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "ui/pixel_ops.h" + +#define LCDIF_MMIO_SIZE (16 * KiB) +#define LCDIF_RESET_CTRL1 0x000f0000 + +REG32(CTRL, 0x00) + FIELD(CTRL, RUN, 0, 1) + FIELD(CTRL, WORD_LENGTH, 8, 2) +REG32(CTRL1, 0x10) + FIELD(CTRL1, CUR_FRAME_DONE_IRQ, 9, 1) + FIELD(CTRL1, CUR_FRAME_DONE_IRQ_EN, 13, 1) + FIELD(CTRL1, BYTE_PACKING_FORMAT, 16, 4) +REG32(V4_TRANSFER_COUNT, 0x30) + FIELD(V4_TRANSFER_COUNT, H_COUNT, 0, 16) + FIELD(V4_TRANSFER_COUNT, V_COUNT, 16, 16) +REG32(V4_CUR_BUF, 0x40) +REG32(V4_NEXT_BUF, 0x50) +REG32(AS_NEXT_BUF, 0x230) + +#define REG_SET 0x4 +#define REG_CLR 0x8 +#define REG_TOG 0xc + +#define CTRL_WORD_LENGTH_16 0 +#define CTRL_WORD_LENGTH_24 3 + +#define FRAME_PERIOD_NS (16 * 1000 * 1000ULL) + +enum IMX6ULLCDIFReg { + IMX6UL_LCDIF_REG_CTRL = A_CTRL >> 4, + IMX6UL_LCDIF_REG_CTRL1 = A_CTRL1 >> 4, + IMX6UL_LCDIF_REG_V4_TRANSFER_COUNT = A_V4_TRANSFER_COUNT >> 4, + IMX6UL_LCDIF_REG_V4_CUR_BUF = A_V4_CUR_BUF >> 4, + IMX6UL_LCDIF_REG_V4_NEXT_BUF = A_V4_NEXT_BUF >> 4, + IMX6UL_LCDIF_REG_AS_NEXT_BUF = A_AS_NEXT_BUF >> 4, +}; + +static inline bool imx6ul_lcdif_reg_exists(hwaddr reg) +{ + return (reg >> 4) < IMX6UL_LCDIF_REGS_NUM; +} + +static inline bool imx6ul_lcdif_reg_has_setclr(hwaddr reg) +{ + switch (reg) { + case A_CTRL: + case A_CTRL1: + return true; + default: + return false; + } +} + +static inline bool imx6ul_lcdif_is_running(IMX6ULLCDIFState *s) +{ + uint32_t ctrl = s->regs[IMX6UL_LCDIF_REG_CTRL]; + + return FIELD_EX32(ctrl, CTRL, RUN); +} + +static inline bool imx6ul_lcdif_frame_done_pending(IMX6ULLCDIFState *s) +{ + uint32_t ctrl1 = s->regs[IMX6UL_LCDIF_REG_CTRL1]; + + return FIELD_EX32(ctrl1, CTRL1, CUR_FRAME_DONE_IRQ); +} + +static void imx6ul_lcdif_schedule_frame(IMX6ULLCDIFState *s) +{ + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + timer_mod(s->frame_timer, now + FRAME_PERIOD_NS); +} + +static void imx6ul_lcdif_maybe_schedule_frame(IMX6ULLCDIFState *s) +{ + if (imx6ul_lcdif_is_running(s) && !imx6ul_lcdif_frame_done_pending(s)) { + imx6ul_lcdif_schedule_frame(s); + } else { + timer_del(s->frame_timer); + } +} + +static void imx6ul_lcdif_update_irq(IMX6ULLCDIFState *s) +{ + uint32_t ctrl1 = s->regs[IMX6UL_LCDIF_REG_CTRL1]; + bool level = FIELD_EX32(ctrl1, CTRL1, CUR_FRAME_DONE_IRQ_EN) && + FIELD_EX32(ctrl1, CTRL1, CUR_FRAME_DONE_IRQ); + + qemu_set_irq(s->irq, level); +} + +static void imx6ul_lcdif_frame_done(IMX6ULLCDIFState *s) +{ + uint32_t ctrl1 = s->regs[IMX6UL_LCDIF_REG_CTRL1]; + + ctrl1 = FIELD_DP32(ctrl1, CTRL1, CUR_FRAME_DONE_IRQ, 1); + s->regs[IMX6UL_LCDIF_REG_CTRL1] = ctrl1; + imx6ul_lcdif_update_irq(s); +} + +static void imx6ul_lcdif_draw_line_rgb565(void *opaque, uint8_t *dst, + const uint8_t *src, int width, + int dststep) +{ + uint32_t *dst32 = (uint32_t *)dst; + int i; + + for (i = 0; i < width; i++) { + uint16_t pixel = lduw_le_p(src); + uint8_t r = ((pixel >> 11) & 0x1f) << 3; + uint8_t g = ((pixel >> 5) & 0x3f) << 2; + uint8_t b = (pixel & 0x1f) << 3; + + *dst32++ = rgb_to_pixel32(r, g, b); + src += 2; + } +} + +static void imx6ul_lcdif_draw_line_xrgb8888(void *opaque, uint8_t *dst, + const uint8_t *src, int width, + int dststep) +{ + uint32_t *dst32 = (uint32_t *)dst; + int i; + + for (i = 0; i < width; i++) { + uint32_t pixel = ldl_le_p(src); + uint8_t r = (pixel >> 16) & 0xff; + uint8_t g = (pixel >> 8) & 0xff; + uint8_t b = pixel & 0xff; + + *dst32++ = rgb_to_pixel32(r, g, b); + src += 4; + } +} + +static void imx6ul_lcdif_update_display(void *opaque) +{ + IMX6ULLCDIFState *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); + uint32_t transfer_count = s->regs[IMX6UL_LCDIF_REG_V4_TRANSFER_COUNT]; + uint32_t width = FIELD_EX32(transfer_count, V4_TRANSFER_COUNT, H_COUNT); + uint32_t height = FIELD_EX32(transfer_count, V4_TRANSFER_COUNT, V_COUNT); + uint32_t ctrl = s->regs[IMX6UL_LCDIF_REG_CTRL]; + uint32_t frame_base = s->regs[IMX6UL_LCDIF_REG_V4_CUR_BUF]; + drawfn fn; + int first = 0; + int last = 0; + int src_width; + + if (!imx6ul_lcdif_is_running(s) || width == 0 || height == 0) { + return; + } + + switch (FIELD_EX32(ctrl, CTRL, WORD_LENGTH)) { + case CTRL_WORD_LENGTH_16: + s->src_bpp = 2; + fn = imx6ul_lcdif_draw_line_rgb565; + break; + case CTRL_WORD_LENGTH_24: + s->src_bpp = 4; + fn = imx6ul_lcdif_draw_line_xrgb8888; + break; + default: + return; + } + + if (surface_width(surface) != width || surface_height(surface) != height) { + qemu_console_resize(s->con, width, height); + surface = qemu_console_surface(s->con); + s->invalidate = true; + } + + src_width = width * s->src_bpp; + if (s->invalidate || s->fb_base != frame_base || + s->src_width != src_width || s->rows != height) { + framebuffer_update_memory_section(&s->fbsection, get_system_memory(), + frame_base, height, src_width); + s->fb_base = frame_base; + s->src_width = src_width; + s->rows = height; + } + + framebuffer_update_display(surface, &s->fbsection, width, height, + src_width, surface_stride(surface), 0, + s->invalidate, fn, s, &first, &last); + if (first >= 0) { + dpy_gfx_update(s->con, 0, first, width, last - first + 1); + } + + s->invalidate = false; +} + +static void imx6ul_lcdif_invalidate_display(void *opaque) +{ + IMX6ULLCDIFState *s = opaque; + + s->invalidate = true; +} + +static const GraphicHwOps imx6ul_lcdif_graphic_ops = { + .invalidate = imx6ul_lcdif_invalidate_display, + .gfx_update = imx6ul_lcdif_update_display, +}; + +static void imx6ul_lcdif_frame_timer_cb(void *opaque) +{ + IMX6ULLCDIFState *s = opaque; + + if (!imx6ul_lcdif_is_running(s) || imx6ul_lcdif_frame_done_pending(s)) { + return; + } + + imx6ul_lcdif_frame_done(s); +} + +static uint64_t imx6ul_lcdif_read(void *opaque, hwaddr offset, unsigned size) +{ + IMX6ULLCDIFState *s = opaque; + hwaddr reg = offset & ~0xf; + uint32_t idx; + + assert(size == 4); + assert(!(offset & 0x3)); + assert(offset < LCDIF_MMIO_SIZE); + + idx = reg >> 4; + if (idx >= ARRAY_SIZE(s->regs)) { + return 0; + } + + return s->regs[idx]; +} + +static void imx6ul_lcdif_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + IMX6ULLCDIFState *s = opaque; + hwaddr reg = offset & ~0xf; + uint32_t idx; + uint32_t oldv; + + assert(size == 4); + assert(!(offset & 0x3)); + assert(offset < LCDIF_MMIO_SIZE); + + if (!imx6ul_lcdif_reg_exists(reg)) { + return; + } + + idx = reg >> 4; + oldv = s->regs[idx]; + + switch (offset & 0xf) { + case 0: + s->regs[idx] = (uint32_t)value; + break; + case REG_SET: + if (!imx6ul_lcdif_reg_has_setclr(reg)) { + return; + } + s->regs[idx] = oldv | (uint32_t)value; + break; + case REG_CLR: + if (!imx6ul_lcdif_reg_has_setclr(reg)) { + return; + } + s->regs[idx] = oldv & ~(uint32_t)value; + break; + case REG_TOG: + if (!imx6ul_lcdif_reg_has_setclr(reg)) { + return; + } + s->regs[idx] = oldv ^ (uint32_t)value; + break; + default: + g_assert_not_reached(); + } + + switch (reg) { + case A_CTRL: + if (!FIELD_EX32(oldv, CTRL, RUN) && + FIELD_EX32(s->regs[idx], CTRL, RUN)) { + s->invalidate = true; + graphic_hw_invalidate(s->con); + imx6ul_lcdif_maybe_schedule_frame(s); + break; + } + if (FIELD_EX32(oldv, CTRL, RUN) && + !FIELD_EX32(s->regs[idx], CTRL, RUN)) { + timer_del(s->frame_timer); + } + break; + case A_CTRL1: + if (FIELD_EX32(oldv, CTRL1, CUR_FRAME_DONE_IRQ) && + !FIELD_EX32(s->regs[idx], CTRL1, CUR_FRAME_DONE_IRQ)) { + imx6ul_lcdif_maybe_schedule_frame(s); + } + break; + case A_V4_TRANSFER_COUNT: + s->invalidate = true; + graphic_hw_invalidate(s->con); + break; + case A_V4_CUR_BUF: + s->invalidate = true; + graphic_hw_invalidate(s->con); + break; + case A_V4_NEXT_BUF: + s->regs[IMX6UL_LCDIF_REG_V4_CUR_BUF] = s->regs[idx]; + imx6ul_lcdif_frame_done(s); + s->invalidate = true; + graphic_hw_invalidate(s->con); + imx6ul_lcdif_maybe_schedule_frame(s); + return; + case A_AS_NEXT_BUF: + imx6ul_lcdif_frame_done(s); + imx6ul_lcdif_maybe_schedule_frame(s); + return; + default: + break; + } + + imx6ul_lcdif_update_irq(s); +} + +static const MemoryRegionOps imx6ul_lcdif_ops = { + .read = imx6ul_lcdif_read, + .write = imx6ul_lcdif_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx6ul_lcdif_reset(DeviceState *dev) +{ + IMX6ULLCDIFState *s = IMX6UL_LCDIF(dev); + + memset(s->regs, 0, sizeof(s->regs)); + s->regs[IMX6UL_LCDIF_REG_CTRL1] = LCDIF_RESET_CTRL1; + s->fb_base = 0; + s->src_width = 0; + s->rows = 0; + s->src_bpp = 0; + s->invalidate = true; + timer_del(s->frame_timer); + imx6ul_lcdif_update_irq(s); +} + +static int imx6ul_lcdif_post_load(void *opaque, int version_id) +{ + IMX6ULLCDIFState *s = opaque; + + s->fb_base = 0; + s->src_width = 0; + s->rows = 0; + s->src_bpp = 0; + s->invalidate = true; + + imx6ul_lcdif_update_irq(s); + if (imx6ul_lcdif_is_running(s) && + !imx6ul_lcdif_frame_done_pending(s) && + !timer_pending(s->frame_timer)) { + imx6ul_lcdif_schedule_frame(s); + } + + return 0; +} + +static const VMStateDescription vmstate_imx6ul_lcdif = { + .name = TYPE_IMX6UL_LCDIF, + .version_id = 1, + .minimum_version_id = 1, + .post_load = imx6ul_lcdif_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, IMX6ULLCDIFState, IMX6UL_LCDIF_REGS_NUM), + VMSTATE_TIMER_PTR(frame_timer, IMX6ULLCDIFState), + VMSTATE_END_OF_LIST() + }, +}; + +static void imx6ul_lcdif_realize(DeviceState *dev, Error **errp) +{ + IMX6ULLCDIFState *s = IMX6UL_LCDIF(dev); + + s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + imx6ul_lcdif_frame_timer_cb, s); + s->invalidate = true; + memory_region_init_io(&s->iomem, OBJECT(dev), &imx6ul_lcdif_ops, s, + TYPE_IMX6UL_LCDIF, LCDIF_MMIO_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); + s->con = graphic_console_init(dev, 0, &imx6ul_lcdif_graphic_ops, s); +} + +static void imx6ul_lcdif_unrealize(DeviceState *dev) +{ + IMX6ULLCDIFState *s = IMX6UL_LCDIF(dev); + + timer_del(s->frame_timer); + timer_free(s->frame_timer); + s->frame_timer = NULL; + + if (s->con) { + graphic_console_close(s->con); + s->con = NULL; + } +} + +static void imx6ul_lcdif_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = imx6ul_lcdif_realize; + dc->unrealize = imx6ul_lcdif_unrealize; + dc->vmsd = &vmstate_imx6ul_lcdif; + device_class_set_legacy_reset(dc, imx6ul_lcdif_reset); + dc->desc = "i.MX6UL LCDIF"; +} + +static const TypeInfo imx6ul_lcdif_info = { + .name = TYPE_IMX6UL_LCDIF, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMX6ULLCDIFState), + .class_init = imx6ul_lcdif_class_init, +}; + +static void imx6ul_lcdif_register_types(void) +{ + type_register_static(&imx6ul_lcdif_info); +} + +type_init(imx6ul_lcdif_register_types) diff --git a/hw/display/meson.build b/hw/display/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -XXX,XX +XXX,XX @@ system_ss.add(when: ['CONFIG_VGA_CIRRUS', 'CONFIG_VGA_ISA'], if_true: files('cir system_ss.add(when: 'CONFIG_G364FB', if_true: files('g364fb.c')) system_ss.add(when: 'CONFIG_JAZZ_LED', if_true: files('jazz_led.c')) system_ss.add(when: 'CONFIG_PL110', if_true: files('pl110.c')) +system_ss.add(when: 'CONFIG_IMX6UL_LCDIF', if_true: files('imx6ul_lcdif.c')) system_ss.add(when: 'CONFIG_SII9022', if_true: files('sii9022.c')) system_ss.add(when: 'CONFIG_SSD0303', if_true: files('ssd0303.c')) system_ss.add(when: 'CONFIG_SSD0323', if_true: files('ssd0323.c')) diff --git a/include/hw/display/imx6ul_lcdif.h b/include/hw/display/imx6ul_lcdif.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/include/hw/display/imx6ul_lcdif.h @@ -XXX,XX +XXX,XX @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * i.MX6UL LCDIF controller + * + * Copyright (c) 2026 Yucai Liu <1486344514@qq.com> + */ + +#ifndef IMX6UL_LCDIF_H +#define IMX6UL_LCDIF_H + +#include "hw/core/sysbus.h" +#include "qom/object.h" +#include "qemu/timer.h" +#include "ui/console.h" + +#define TYPE_IMX6UL_LCDIF "imx6ul-lcdif" +#define IMX6UL_LCDIF_REGS_NUM ((0x230 >> 4) + 1) +OBJECT_DECLARE_SIMPLE_TYPE(IMX6ULLCDIFState, IMX6UL_LCDIF) + +struct IMX6ULLCDIFState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + MemoryRegionSection fbsection; + qemu_irq irq; + QemuConsole *con; + QEMUTimer *frame_timer; + uint32_t fb_base; + uint32_t src_width; + uint32_t rows; + uint8_t src_bpp; + bool invalidate; + uint32_t regs[IMX6UL_LCDIF_REGS_NUM]; +}; + +#endif /* IMX6UL_LCDIF_H */ -- 2.43.0
From: Yucai Liu <1486344514@qq.com> Instantiate LCDIF as a child object of the i.MX6UL SoC in init and realize it in the SoC realize path before MMIO/IRQ hookup. Also make FSL_IMX6UL select CONFIG_IMX6UL_LCDIF and map the LCDIF region with a 16 KiB size to match the SoC memory map. Signed-off-by: Yucai Liu <1486344514@qq.com> Message-id: 20260412110240.93116-3-yangyanglan718@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 1 + hw/arm/fsl-imx6ul.c | 12 ++++++++++-- include/hw/arm/fsl-imx6ul.h | 4 +++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX6UL imply I2C_DEVICES select A15MPCORE select IMX + select IMX6UL_LCDIF select IMX_FEC select IMX_I2C select IMX_USBPHY diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -XXX,XX +XXX,XX @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx6ul.h" +#include "hw/display/imx6ul_lcdif.h" #include "hw/misc/unimp.h" #include "hw/usb/imx-usb-phy.h" #include "hw/core/boards.h" @@ -XXX,XX +XXX,XX @@ static void fsl_imx6ul_init(Object *obj) object_initialize_child(obj, name, &s->usb[i], TYPE_CHIPIDEA); } + /* + * LCDIF + */ + object_initialize_child(obj, "lcdif", &s->lcdif, TYPE_IMX6UL_LCDIF); + /* * SDHCIs */ @@ -XXX,XX +XXX,XX @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) /* * LCD */ - create_unimplemented_device("lcdif", FSL_IMX6UL_LCDIF_ADDR, - FSL_IMX6UL_LCDIF_SIZE); + sysbus_realize(SYS_BUS_DEVICE(&s->lcdif), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->lcdif), 0, FSL_IMX6UL_LCDIF_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lcdif), 0, + qdev_get_gpio_in(gic, FSL_IMX6UL_LCDIF_IRQ)); /* * CSU diff --git a/include/hw/arm/fsl-imx6ul.h b/include/hw/arm/fsl-imx6ul.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx6ul.h +++ b/include/hw/arm/fsl-imx6ul.h @@ -XXX,XX +XXX,XX @@ #include "hw/net/imx_fec.h" #include "hw/usb/chipidea.h" #include "hw/usb/imx-usb-phy.h" +#include "hw/display/imx6ul_lcdif.h" #include "system/memory.h" #include "target/arm/cpu.h" #include "qom/object.h" @@ -XXX,XX +XXX,XX @@ struct FslIMX6ULState { IMX2WdtState wdt[FSL_IMX6UL_NUM_WDTS]; IMXUSBPHYState usbphy[FSL_IMX6UL_NUM_USB_PHYS]; ChipideaState usb[FSL_IMX6UL_NUM_USBS]; + IMX6ULLCDIFState lcdif; MemoryRegion rom; MemoryRegion caam; MemoryRegion ocram; @@ -XXX,XX +XXX,XX @@ enum FslIMX6ULMemoryMap { FSL_IMX6UL_PXP_SIZE = (16 * KiB), FSL_IMX6UL_LCDIF_ADDR = 0x021C8000, - FSL_IMX6UL_LCDIF_SIZE = 0x100, + FSL_IMX6UL_LCDIF_SIZE = (16 * KiB), FSL_IMX6UL_CSI_ADDR = 0x021C4000, FSL_IMX6UL_CSI_SIZE = 0x100, -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> We have a registerfields interface which we can use for defining fields alongside helpers to access them. Define the basic syndrome layout and convert the helpers that take the imm16 data directly. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-2-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 75 ++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ #ifndef TARGET_ARM_SYNDROME_H #define TARGET_ARM_SYNDROME_H -#include "qemu/bitops.h" +#include "hw/core/registerfields.h" /* Valid Syndrome Register EC field values */ enum arm_exception_class { @@ -XXX,XX +XXX,XX @@ enum arm_exception_class { EC_AA64_BKPT = 0x3c, }; +/* Generic syndrome encoding layout for HSR and lower 32 bits of ESR_EL2 */ +FIELD(SYNDROME, EC, 26, 6) +FIELD(SYNDROME, IL, 25, 1) +FIELD(SYNDROME, ISS, 0, 25) + typedef enum { SME_ET_AccessTrap, SME_ET_Streaming, @@ -XXX,XX +XXX,XX @@ typedef enum { static inline uint32_t syn_get_ec(uint32_t syn) { - return syn >> ARM_EL_EC_SHIFT; + return FIELD_EX32(syn, SYNDROME, EC); } static inline uint32_t syn_set_ec(uint32_t syn, uint32_t ec) { - return deposit32(syn, ARM_EL_EC_SHIFT, ARM_EL_EC_LENGTH, ec); + return FIELD_DP32(syn, SYNDROME, EC, ec); } /* @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_set_ec(uint32_t syn, uint32_t ec) */ static inline uint32_t syn_uncategorized(void) { - return (EC_UNCATEGORIZED << ARM_EL_EC_SHIFT) | ARM_EL_IL; + uint32_t res = syn_set_ec(0, EC_UNCATEGORIZED); + res = FIELD_DP32(res, SYNDROME, IL, 1); + return res; } +FIELD(ISS_IMM16, IMM16, 0, 16) + static inline uint32_t syn_aa64_svc(uint32_t imm16) { - return (EC_AA64_SVC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff); + uint32_t res = syn_set_ec(0, EC_AA64_SVC); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); + return res; } static inline uint32_t syn_aa64_hvc(uint32_t imm16) { - return (EC_AA64_HVC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff); + uint32_t res = syn_set_ec(0, EC_AA64_HVC); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); + return res; } static inline uint32_t syn_aa64_smc(uint32_t imm16) { - return (EC_AA64_SMC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff); + uint32_t res = syn_set_ec(0, EC_AA64_SMC); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); + return res; } static inline uint32_t syn_aa32_svc(uint32_t imm16, bool is_16bit) { - return (EC_AA32_SVC << ARM_EL_EC_SHIFT) | (imm16 & 0xffff) - | (is_16bit ? 0 : ARM_EL_IL); + uint32_t res = syn_set_ec(0, EC_AA32_SVC); + res = FIELD_DP32(res, SYNDROME, IL, is_16bit ? 0 : 1); + res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); + return res; } static inline uint32_t syn_aa32_hvc(uint32_t imm16) { - return (EC_AA32_HVC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff); + uint32_t res = syn_set_ec(0, EC_AA32_HVC); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); + return res; } static inline uint32_t syn_aa32_smc(void) { - return (EC_AA32_SMC << ARM_EL_EC_SHIFT) | ARM_EL_IL; + uint32_t res = syn_set_ec(0, EC_AA32_SMC); + res = FIELD_DP32(res, SYNDROME, IL, 1); + return res; } static inline uint32_t syn_aa64_bkpt(uint32_t imm16) { - return (EC_AA64_BKPT << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff); + uint32_t res = syn_set_ec(0, EC_AA64_BKPT); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); + return res; } static inline uint32_t syn_aa32_bkpt(uint32_t imm16, bool is_16bit) { - return (EC_AA32_BKPT << ARM_EL_EC_SHIFT) | (imm16 & 0xffff) - | (is_16bit ? 0 : ARM_EL_IL); + uint32_t res = syn_set_ec(0, EC_AA32_BKPT); + res = FIELD_DP32(res, SYNDROME, IL, is_16bit ? 0 : 1); + res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); + return res; } static inline uint32_t syn_aa64_sysregtrap(int op0, int op1, int op2, @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_simd_access_trap(int cv, int cond, bool is_16bit) static inline uint32_t syn_sve_access_trap(void) { - return (EC_SVEACCESSTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL; + uint32_t res = syn_set_ec(0, EC_SVEACCESSTRAP); + res = FIELD_DP32(res, SYNDROME, IL, 1); + return res; } /* @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_wfx(int cv, int cond, int ti, bool is_16bit) static inline uint32_t syn_illegalstate(void) { - return (EC_ILLEGALSTATE << ARM_EL_EC_SHIFT) | ARM_EL_IL; + uint32_t res = syn_set_ec(0, EC_ILLEGALSTATE); + res = FIELD_DP32(res, SYNDROME, IL, 1); + return res; } static inline uint32_t syn_pcalignment(void) { - return (EC_PCALIGNMENT << ARM_EL_EC_SHIFT) | ARM_EL_IL; + uint32_t res = syn_set_ec(0, EC_PCALIGNMENT); + res = FIELD_DP32(res, SYNDROME, IL, 1); + return res; } static inline uint32_t syn_gcs_data_check(GCSInstructionType it, int rn) @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_gcs_gcsstr(int ra, int rn) static inline uint32_t syn_serror(uint32_t extra) { - return (EC_SERROR << ARM_EL_EC_SHIFT) | ARM_EL_IL | extra; + uint32_t res = syn_set_ec(0, EC_SERROR); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, SYNDROME, ISS, extra); + return res; } static inline uint32_t syn_mop(bool is_set, bool is_setg, int options, -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Migrate syn_aa64_sysregtrap and co-processor register trap syndromes to the registerfields API. The co-processor syndromes are split between single and duel register moves. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-3-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 124 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 22 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ enum arm_exception_class { /* Generic syndrome encoding layout for HSR and lower 32 bits of ESR_EL2 */ FIELD(SYNDROME, EC, 26, 6) -FIELD(SYNDROME, IL, 25, 1) +FIELD(SYNDROME, IL, 25, 1) /* IL=1 for 32 bit instructions */ FIELD(SYNDROME, ISS, 0, 25) typedef enum { @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_aa64_smc(uint32_t imm16) static inline uint32_t syn_aa32_svc(uint32_t imm16, bool is_16bit) { uint32_t res = syn_set_ec(0, EC_AA32_SVC); - res = FIELD_DP32(res, SYNDROME, IL, is_16bit ? 0 : 1); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); return res; } @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_aa64_bkpt(uint32_t imm16) static inline uint32_t syn_aa32_bkpt(uint32_t imm16, bool is_16bit) { uint32_t res = syn_set_ec(0, EC_AA32_BKPT); - res = FIELD_DP32(res, SYNDROME, IL, is_16bit ? 0 : 1); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); return res; } +/* + * ISS encoding for an exception from MSR, MRS, or System instruction + * in AArch64 state. + */ +FIELD(SYSREG_ISS, ISREAD, 0, 1) /* Direction, 1 is read */ +FIELD(SYSREG_ISS, CRM, 1, 4) +FIELD(SYSREG_ISS, RT, 5, 5) +FIELD(SYSREG_ISS, CRN, 10, 4) +FIELD(SYSREG_ISS, OP1, 14, 3) +FIELD(SYSREG_ISS, OP2, 17, 3) +FIELD(SYSREG_ISS, OP0, 20, 2) + static inline uint32_t syn_aa64_sysregtrap(int op0, int op1, int op2, int crn, int crm, int rt, int isread) { - return (EC_SYSTEMREGISTERTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL - | (op0 << 20) | (op2 << 17) | (op1 << 14) | (crn << 10) | (rt << 5) - | (crm << 1) | isread; + uint32_t res = syn_set_ec(0, EC_SYSTEMREGISTERTRAP); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, SYSREG_ISS, OP0, op0); + res = FIELD_DP32(res, SYSREG_ISS, OP2, op2); + res = FIELD_DP32(res, SYSREG_ISS, OP1, op1); + res = FIELD_DP32(res, SYSREG_ISS, CRN, crn); + res = FIELD_DP32(res, SYSREG_ISS, RT, rt); + res = FIELD_DP32(res, SYSREG_ISS, CRM, crm); + res = FIELD_DP32(res, SYSREG_ISS, ISREAD, isread); + + return res; } +/* + * ISS encoding for an exception from an MCR or MRC access + * (move to/from co-processor) + */ +FIELD(COPROC_ISS, ISREAD, 0, 1) +FIELD(COPROC_ISS, CRM, 1, 4) +FIELD(COPROC_ISS, RT, 5, 5) +FIELD(COPROC_ISS, CRN, 10, 4) +FIELD(COPROC_ISS, OP1, 14, 3) +FIELD(COPROC_ISS, OP2, 17, 3) +FIELD(COPROC_ISS, COND, 20, 4) +FIELD(COPROC_ISS, CV, 24, 1) + static inline uint32_t syn_cp14_rt_trap(int cv, int cond, int opc1, int opc2, int crn, int crm, int rt, int isread, bool is_16bit) { - return (EC_CP14RTTRAP << ARM_EL_EC_SHIFT) - | (is_16bit ? 0 : ARM_EL_IL) - | (cv << 24) | (cond << 20) | (opc2 << 17) | (opc1 << 14) - | (crn << 10) | (rt << 5) | (crm << 1) | isread; + uint32_t res = syn_set_ec(0, EC_CP14RTTRAP); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); + + res = FIELD_DP32(res, COPROC_ISS, CV, cv); + res = FIELD_DP32(res, COPROC_ISS, COND, cond); + res = FIELD_DP32(res, COPROC_ISS, OP2, opc2); + res = FIELD_DP32(res, COPROC_ISS, OP1, opc1); + res = FIELD_DP32(res, COPROC_ISS, CRN, crn); + res = FIELD_DP32(res, COPROC_ISS, RT, rt); + res = FIELD_DP32(res, COPROC_ISS, CRM, crm); + res = FIELD_DP32(res, COPROC_ISS, ISREAD, isread); + + return res; } static inline uint32_t syn_cp15_rt_trap(int cv, int cond, int opc1, int opc2, int crn, int crm, int rt, int isread, bool is_16bit) { - return (EC_CP15RTTRAP << ARM_EL_EC_SHIFT) - | (is_16bit ? 0 : ARM_EL_IL) - | (cv << 24) | (cond << 20) | (opc2 << 17) | (opc1 << 14) - | (crn << 10) | (rt << 5) | (crm << 1) | isread; + uint32_t res = syn_set_ec(0, EC_CP15RTTRAP); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); + + res = FIELD_DP32(res, COPROC_ISS, CV, cv); + res = FIELD_DP32(res, COPROC_ISS, COND, cond); + res = FIELD_DP32(res, COPROC_ISS, OP2, opc2); + res = FIELD_DP32(res, COPROC_ISS, OP1, opc1); + res = FIELD_DP32(res, COPROC_ISS, CRN, crn); + res = FIELD_DP32(res, COPROC_ISS, RT, rt); + res = FIELD_DP32(res, COPROC_ISS, CRM, crm); + res = FIELD_DP32(res, COPROC_ISS, ISREAD, isread); + + return res; } +/* + * ISS encoding for an exception from an MCRR or MRRC access + * (move to/from co-processor with 2 regs) + */ +FIELD(COPROC_R2_ISS, ISREAD, 0, 1) +FIELD(COPROC_R2_ISS, CRM, 1, 4) +FIELD(COPROC_R2_ISS, RT, 5, 5) +FIELD(COPROC_R2_ISS, RT2, 10, 5) +FIELD(COPROC_R2_ISS, OP1, 16, 4) +FIELD(COPROC_R2_ISS, COND, 20, 4) +FIELD(COPROC_R2_ISS, CV, 24, 1) + static inline uint32_t syn_cp14_rrt_trap(int cv, int cond, int opc1, int crm, int rt, int rt2, int isread, bool is_16bit) { - return (EC_CP14RRTTRAP << ARM_EL_EC_SHIFT) - | (is_16bit ? 0 : ARM_EL_IL) - | (cv << 24) | (cond << 20) | (opc1 << 16) - | (rt2 << 10) | (rt << 5) | (crm << 1) | isread; + uint32_t res = syn_set_ec(0, EC_CP14RRTTRAP); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); + + res = FIELD_DP32(res, COPROC_R2_ISS, CV, cv); + res = FIELD_DP32(res, COPROC_R2_ISS, COND, cond); + res = FIELD_DP32(res, COPROC_R2_ISS, OP1, opc1); + res = FIELD_DP32(res, COPROC_R2_ISS, RT2, rt2); + res = FIELD_DP32(res, COPROC_R2_ISS, RT, rt); + res = FIELD_DP32(res, COPROC_R2_ISS, CRM, crm); + res = FIELD_DP32(res, COPROC_R2_ISS, ISREAD, isread); + + return res; } static inline uint32_t syn_cp15_rrt_trap(int cv, int cond, int opc1, int crm, int rt, int rt2, int isread, bool is_16bit) { - return (EC_CP15RRTTRAP << ARM_EL_EC_SHIFT) - | (is_16bit ? 0 : ARM_EL_IL) - | (cv << 24) | (cond << 20) | (opc1 << 16) - | (rt2 << 10) | (rt << 5) | (crm << 1) | isread; + uint32_t res = syn_set_ec(0, EC_CP15RRTTRAP); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); + + res = FIELD_DP32(res, COPROC_R2_ISS, CV, cv); + res = FIELD_DP32(res, COPROC_R2_ISS, COND, cond); + res = FIELD_DP32(res, COPROC_R2_ISS, OP1, opc1); + res = FIELD_DP32(res, COPROC_R2_ISS, RT2, rt2); + res = FIELD_DP32(res, COPROC_R2_ISS, RT, rt); + res = FIELD_DP32(res, COPROC_R2_ISS, CRM, crm); + res = FIELD_DP32(res, COPROC_R2_ISS, ISREAD, isread); + + return res; } static inline uint32_t syn_fp_access_trap(int cv, int cond, bool is_16bit, -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> The syn_simd_access trap was never used so remove it. We should only see the COPROC encoding on v7 architectures. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-4-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_cp15_rrt_trap(int cv, int cond, int opc1, int crm, return res; } +/* + * ISS encoding for an exception from an access to a register of + * instruction resulting from the FPEN or TFP traps. + */ +FIELD(FP_ISS, COPROC, 0, 4) /* ARMv7 only */ +FIELD(FP_ISS, COND, 20, 4) +FIELD(FP_ISS, CV, 24, 1) + static inline uint32_t syn_fp_access_trap(int cv, int cond, bool is_16bit, int coproc) { /* AArch32 FP trap or any AArch64 FP/SIMD trap: TA == 0 */ - return (EC_ADVSIMDFPACCESSTRAP << ARM_EL_EC_SHIFT) - | (is_16bit ? 0 : ARM_EL_IL) - | (cv << 24) | (cond << 20) | coproc; -} + uint32_t res = syn_set_ec(0, EC_ADVSIMDFPACCESSTRAP); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); -static inline uint32_t syn_simd_access_trap(int cv, int cond, bool is_16bit) -{ - /* AArch32 SIMD trap: TA == 1 coproc == 0 */ - return (EC_ADVSIMDFPACCESSTRAP << ARM_EL_EC_SHIFT) - | (is_16bit ? 0 : ARM_EL_IL) - | (cv << 24) | (cond << 20) | (1 << 5); + res = FIELD_DP32(res, FP_ISS, CV, cv); + res = FIELD_DP32(res, FP_ISS, COND, cond); + res = FIELD_DP32(res, FP_ISS, COPROC, coproc); + + return res; } static inline uint32_t syn_sve_access_trap(void) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> For simplicity keep the OP as a two bit field rather than the two interlinked fields in the docs (ERET/ERETA). Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-5-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_sve_access_trap(void) } /* + * ISS encoding for an exception from an ERET, ERETAA or ERETAB + * instructions. + * * eret_op is bits [1:0] of the ERET instruction, so: * 0 for ERET, 2 for ERETAA, 3 for ERETAB. */ +FIELD(ERET_ISS, OP, 0, 2) + static inline uint32_t syn_erettrap(int eret_op) { - return (EC_ERETTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL | eret_op; + uint32_t res = syn_set_ec(0, EC_ERETTRAP); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, ERET_ISS, OP, eret_op); + + return res; } static inline uint32_t syn_smetrap(SMEExceptionType etype, bool is_16bit) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-6-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_erettrap(int eret_op) return res; } +/* + * ISS encoding for an exception due to SME functionality + */ +FIELD(SME_ISS, SMTC, 0, 2) + static inline uint32_t syn_smetrap(SMEExceptionType etype, bool is_16bit) { - return (EC_SMETRAP << ARM_EL_EC_SHIFT) - | (is_16bit ? 0 : ARM_EL_IL) | etype; + uint32_t res = syn_set_ec(0, EC_SMETRAP); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); + res = FIELD_DP32(res, SME_ISS, SMTC, etype); + + return res; } static inline uint32_t syn_pacfail(bool data, int keynumber) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> syn_pactrap is fairly simple as the ISS is all RES0. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-7-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_smetrap(SMEExceptionType etype, bool is_16bit) return res; } +/* + * ISS encoding for a PAC Fail exceptions + */ +FIELD(PACFAIL_ISS, BnA, 0, 1) /* B key or A key */ +FIELD(PACFAIL_ISS, DnI, 1, 1) /* Data or Instruction */ + static inline uint32_t syn_pacfail(bool data, int keynumber) { - int error_code = (data << 1) | keynumber; - return (EC_PACFAIL << ARM_EL_EC_SHIFT) | ARM_EL_IL | error_code; + uint32_t res = syn_set_ec(0, EC_PACFAIL); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, PACFAIL_ISS, DnI, data); + res = FIELD_DP32(res, PACFAIL_ISS, BnA, keynumber); + + return res; } +/* + * ISS encoding for an exception from a trapped Pointer + * Authentication instruction is RES0 + */ static inline uint32_t syn_pactrap(void) { - return (EC_PACTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL; + uint32_t res = syn_set_ec(0, EC_PACTRAP); + res = FIELD_DP32(res, SYNDROME, IL, 1); + return res; } static inline uint32_t syn_btitrap(int btype) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-8-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_pactrap(void) return res; } +/* + * ISS encoding for an exception from a Branch Target Identification + * instruction. + */ +FIELD(BTI_ISS, BTYPE, 0, 2) + static inline uint32_t syn_btitrap(int btype) { - return (EC_BTITRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL | btype; + uint32_t res = syn_set_ec(0, EC_BTITRAP); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, BTI_ISS, BTYPE, btype); + return res; } static inline uint32_t syn_bxjtrap(int cv, int cond, int rm) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> This is an Armv7 specific syndrome for chips with Jazelle functionality. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-9-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_btitrap(int btype) return res; } +/* + * ISS encoding for trapped BXJ execution + * + * This is an Armv7 encoding. + */ +FIELD(BXJ_ISS, RM, 0, 4) +/* bits 4:19 are Reserved, UNK/SBZP */ +FIELD(BXJ_ISS, COND, 20, 4) +FIELD(BXJ_ISS, CV, 24, 1) + static inline uint32_t syn_bxjtrap(int cv, int cond, int rm) { - return (EC_BXJTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL | - (cv << 24) | (cond << 20) | rm; + uint32_t res = syn_set_ec(0, EC_BXJTRAP); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, BXJ_ISS, CV, cv); + res = FIELD_DP32(res, BXJ_ISS, COND, cond); + res = FIELD_DP32(res, BXJ_ISS, RM, rm); + + return res; } static inline uint32_t syn_gpc(int s2ptw, int ind, int gpcsc, int vncr, -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-10-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_bxjtrap(int cv, int cond, int rm) return res; } +/* + * ISS encoding for a Granule Protection Check exception + * + * These are only reported to EL3 + */ +FIELD(GPC_ISS, xFSC, 0, 6) +FIELD(GPC_ISS, WnR, 6, 1) /* Write not Read */ +FIELD(GPC_ISS, S1PTW, 7, 1) +FIELD(GPC_ISS, CM, 8, 1) +FIELD(GPC_ISS, VNCR, 13, 1) +FIELD(GPC_ISS, GPCSC, 14, 6) +FIELD(GPC_ISS, InD, 20, 1) /* Instruction not Data access */ +FIELD(GPC_ISS, S2PTW, 21, 1) + static inline uint32_t syn_gpc(int s2ptw, int ind, int gpcsc, int vncr, int cm, int s1ptw, int wnr, int fsc) { - return (EC_GPC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (s2ptw << 21) - | (ind << 20) | (gpcsc << 14) | (vncr << 13) | (cm << 8) - | (s1ptw << 7) | (wnr << 6) | fsc; + uint32_t res = syn_set_ec(0, EC_GPC); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, GPC_ISS, S2PTW, s2ptw); + res = FIELD_DP32(res, GPC_ISS, InD, ind); + res = FIELD_DP32(res, GPC_ISS, GPCSC, gpcsc); + res = FIELD_DP32(res, GPC_ISS, VNCR, vncr); + res = FIELD_DP32(res, GPC_ISS, CM, cm); + res = FIELD_DP32(res, GPC_ISS, S1PTW, s1ptw); + res = FIELD_DP32(res, GPC_ISS, WnR, wnr); + res = FIELD_DP32(res, GPC_ISS, xFSC, fsc); + + return res; } static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Migrate syn_insn_abort and syn_data_abort_* to the registerfields API. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260422125250.1303100-11-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 87 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 13 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_gpc(int s2ptw, int ind, int gpcsc, int vncr, return res; } +/* + * ISS encoding for an exception from an Instruction Abort + * + * (aka instruction abort) + */ +FIELD(IABORT_ISS, IFSC, 0, 6) +FIELD(IABORT_ISS, S1PTW, 7, 1) +FIELD(IABORT_ISS, EA, 9, 1) +FIELD(IABORT_ISS, FnV, 10, 1) /* FAR not Valid */ +FIELD(IABORT_ISS, SET, 11, 2) +FIELD(IABORT_ISS, PFV, 14, 1) +FIELD(IABORT_ISS, TopLevel, 21, 1) /* FEAT_THE */ + static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc) { - return (EC_INSNABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) - | ARM_EL_IL | (ea << 9) | (s1ptw << 7) | fsc; + uint32_t res = syn_set_ec(0, EC_INSNABORT + same_el); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, IABORT_ISS, EA, ea); + res = FIELD_DP32(res, IABORT_ISS, S1PTW, s1ptw); + res = FIELD_DP32(res, IABORT_ISS, IFSC, fsc); + + return res; } +/* + * ISS encoding for an exception from a Data Abort + */ +FIELD(DABORT_ISS, DFSC, 0, 6) +FIELD(DABORT_ISS, WNR, 6, 1) +FIELD(DABORT_ISS, S1PTW, 7, 1) +FIELD(DABORT_ISS, CM, 8, 1) +FIELD(DABORT_ISS, EA, 9, 1) +FIELD(DABORT_ISS, FnV, 10, 1) +FIELD(DABORT_ISS, LST, 11, 2) +FIELD(DABORT_ISS, VNCR, 13, 1) +FIELD(DABORT_ISS, AR, 14, 1) +FIELD(DABORT_ISS, SF, 15, 1) +FIELD(DABORT_ISS, SRT, 16, 5) +FIELD(DABORT_ISS, SSE, 21, 1) +FIELD(DABORT_ISS, SAS, 22, 2) +FIELD(DABORT_ISS, ISV, 24, 1) + static inline uint32_t syn_data_abort_no_iss(int same_el, int fnv, int ea, int cm, int s1ptw, int wnr, int fsc) { - return (EC_DATAABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) - | ARM_EL_IL - | (fnv << 10) | (ea << 9) | (cm << 8) | (s1ptw << 7) - | (wnr << 6) | fsc; + uint32_t res = syn_set_ec(0, EC_DATAABORT + same_el); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, DABORT_ISS, FnV, fnv); + res = FIELD_DP32(res, DABORT_ISS, EA, ea); + res = FIELD_DP32(res, DABORT_ISS, CM, cm); + res = FIELD_DP32(res, DABORT_ISS, S1PTW, s1ptw); + res = FIELD_DP32(res, DABORT_ISS, WNR, wnr); + res = FIELD_DP32(res, DABORT_ISS, DFSC, fsc); + + return res; } static inline uint32_t syn_data_abort_with_iss(int same_el, @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_data_abort_with_iss(int same_el, int wnr, int fsc, bool is_16bit) { - return (EC_DATAABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) - | (is_16bit ? 0 : ARM_EL_IL) - | ARM_EL_ISV | (sas << 22) | (sse << 21) | (srt << 16) - | (sf << 15) | (ar << 14) - | (ea << 9) | (cm << 8) | (s1ptw << 7) | (wnr << 6) | fsc; + uint32_t res = syn_set_ec(0, EC_DATAABORT + same_el); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); + + res = FIELD_DP32(res, DABORT_ISS, ISV, 1); + res = FIELD_DP32(res, DABORT_ISS, SAS, sas); + res = FIELD_DP32(res, DABORT_ISS, SSE, sse); + res = FIELD_DP32(res, DABORT_ISS, SRT, srt); + res = FIELD_DP32(res, DABORT_ISS, SF, sf); + res = FIELD_DP32(res, DABORT_ISS, AR, ar); + res = FIELD_DP32(res, DABORT_ISS, EA, ea); + res = FIELD_DP32(res, DABORT_ISS, CM, cm); + res = FIELD_DP32(res, DABORT_ISS, S1PTW, s1ptw); + res = FIELD_DP32(res, DABORT_ISS, WNR, wnr); + res = FIELD_DP32(res, DABORT_ISS, DFSC, fsc); + + return res; } /* @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_data_abort_with_iss(int same_el, */ static inline uint32_t syn_data_abort_vncr(int ea, int wnr, int fsc) { - return (EC_DATAABORT << ARM_EL_EC_SHIFT) | (1 << ARM_EL_EC_SHIFT) - | ARM_EL_IL | ARM_EL_VNCR | (wnr << 6) | fsc; + uint32_t res = syn_set_ec(0, EC_DATAABORT_SAME_EL); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, DABORT_ISS, VNCR, 1); + res = FIELD_DP32(res, DABORT_ISS, WNR, wnr); + res = FIELD_DP32(res, DABORT_ISS, DFSC, fsc); + + return res; } static inline uint32_t syn_swstep(int same_el, int isv, int ex) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Migrate syn_swstep, syn_watchpoint and syn_breakpoint to the registerfields API. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-12-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 54 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_data_abort_vncr(int ea, int wnr, int fsc) return res; } +/* + * ISS encoding for an exception from a Software Step exception. + */ +FIELD(SOFTSTEP_ISS, IFSC, 0, 6) +FIELD(SOFTSTEP_ISS, EX, 6, 1) +FIELD(SOFTSTEP_ISS, ISV, 24, 1) + static inline uint32_t syn_swstep(int same_el, int isv, int ex) { - return (EC_SOFTWARESTEP << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) - | ARM_EL_IL | (isv << 24) | (ex << 6) | 0x22; + uint32_t res = syn_set_ec(0, EC_SOFTWARESTEP + same_el); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, SOFTSTEP_ISS, ISV, isv); + res = FIELD_DP32(res, SOFTSTEP_ISS, EX, ex); + res = FIELD_DP32(res, SOFTSTEP_ISS, IFSC, 0x22); + + return res; } +/* + * ISS encoding for an exception from a Watchpoint exception + */ +FIELD(WATCHPOINT_ISS, DFSC, 0, 6) +FIELD(WATCHPOINT_ISS, WNR, 6, 1) +FIELD(WATCHPOINT_ISS, CM, 8, 1) +FIELD(WATCHPOINT_ISS, FnV, 10, 1) +FIELD(WATCHPOINT_ISS, VNCR, 13, 1) /* FEAT_NV2 */ +FIELD(WATCHPOINT_ISS, FnP, 15, 1) +FIELD(WATCHPOINT_ISS, WPF, 16, 1) +/* bellow mandatory from FEAT_Debugv8p9 */ +FIELD(WATCHPOINT_ISS, WPTV, 17, 1) /* FEAT_Debugv8p2 - WPT valid */ +FIELD(WATCHPOINT_ISS, WPT, 18, 6) /* FEAT_Debugv8p2 - missing WP number */ + static inline uint32_t syn_watchpoint(int same_el, int cm, int wnr) { - return (EC_WATCHPOINT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) - | ARM_EL_IL | (cm << 8) | (wnr << 6) | 0x22; + uint32_t res = syn_set_ec(0, EC_WATCHPOINT + same_el); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, WATCHPOINT_ISS, CM, cm); + res = FIELD_DP32(res, WATCHPOINT_ISS, WNR, wnr); + res = FIELD_DP32(res, WATCHPOINT_ISS, DFSC, 0x22); + + return res; } +/* + * ISS encoding for an exception from a Breakpoint or a Vector Catch + * debug exception. + */ +FIELD(BREAKPOINT_ISS, IFSC, 0, 6) + static inline uint32_t syn_breakpoint(int same_el) { - return (EC_BREAKPOINT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) - | ARM_EL_IL | 0x22; + uint32_t res = syn_set_ec(0, EC_BREAKPOINT + same_el); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, BREAKPOINT_ISS, IFSC, 0x22); + + return res; } static inline uint32_t syn_wfx(int cv, int cond, int ti, bool is_16bit) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> This will help later when we expand the fields we report. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-13-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_breakpoint(int same_el) return res; } +/* + * ISS encoding for an exception from a WF* instruction + */ +FIELD(WFX_ISS, TI, 0, 2) +FIELD(WFX_ISS, RV, 2, 1) +FIELD(WFX_ISS, RN, 5, 5) +FIELD(WFX_ISS, COND, 20, 4) +FIELD(WFX_ISS, CV, 24, 1) + static inline uint32_t syn_wfx(int cv, int cond, int ti, bool is_16bit) { - return (EC_WFX_TRAP << ARM_EL_EC_SHIFT) | - (is_16bit ? 0 : (1 << ARM_EL_IL_SHIFT)) | - (cv << 24) | (cond << 20) | ti; + uint32_t res = syn_set_ec(0, EC_WFX_TRAP); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); + + res = FIELD_DP32(res, WFX_ISS, CV, cv); + res = FIELD_DP32(res, WFX_ISS, COND, cond); + res = FIELD_DP32(res, WFX_ISS, TI, ti); + + return res; } static inline uint32_t syn_illegalstate(void) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Tweak arg names to make it clear raddr is the data address register number. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-14-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_pcalignment(void) return res; } +/* + * ISS encoding for a GCS exception + * + * Field validity depends on EXTYPE + */ +FIELD(GCS_ISS, IT, 0, 5) +FIELD(GCS_ISS, RN, 5, 5) /* only for non EXLOCK exceptions */ +FIELD(GCS_ISS, RADDR, 10, 5) /* only for GCSSTR/GCSSTTR traps */ +FIELD(GCS_ISS, EXTYPE, 20, 4) + static inline uint32_t syn_gcs_data_check(GCSInstructionType it, int rn) { - return ((EC_GCS << ARM_EL_EC_SHIFT) | ARM_EL_IL | - (GCS_ET_DataCheck << 20) | (rn << 5) | it); + uint32_t res = syn_set_ec(0, EC_GCS); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, GCS_ISS, EXTYPE, GCS_ET_DataCheck); + res = FIELD_DP32(res, GCS_ISS, RN, rn); + res = FIELD_DP32(res, GCS_ISS, IT, it); + + return res; } static inline uint32_t syn_gcs_exlock(void) { - return (EC_GCS << ARM_EL_EC_SHIFT) | ARM_EL_IL | (GCS_ET_EXLOCK << 20); + uint32_t res = syn_set_ec(0, EC_GCS); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, GCS_ISS, EXTYPE, GCS_ET_EXLOCK); + + return res; } -static inline uint32_t syn_gcs_gcsstr(int ra, int rn) +static inline uint32_t syn_gcs_gcsstr(int raddr, int rn) { - return ((EC_GCS << ARM_EL_EC_SHIFT) | ARM_EL_IL | - (GCS_ET_GCSSTR_GCSSTTR << 20) | (ra << 10) | (rn << 5)); + uint32_t res = syn_set_ec(0, EC_GCS); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, GCS_ISS, EXTYPE, GCS_ET_GCSSTR_GCSSTTR); + res = FIELD_DP32(res, GCS_ISS, RADDR, raddr); + res = FIELD_DP32(res, GCS_ISS, RN, rn); + + return res; } static inline uint32_t syn_serror(uint32_t extra) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-15-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_serror(uint32_t extra) return res; } +/* + * ISS encoding for an exception from the Memory Copy and Memory Set + * instructions. + */ +FIELD(MOP_ISS, SIZEREG, 0, 5) +FIELD(MOP_ISS, SRCREG, 5, 5) +FIELD(MOP_ISS, DESTREG, 10, 5) +FIELD(MOP_ISS, FORMATOPT, 16, 2) +FIELD(MOP_ISS, OPT_A, 16, 1) +FIELD(MOP_ISS, WRONG_OPT, 17, 1) +FIELD(MOP_ISS, EPILOGUE, 18, 1) +FIELD(MOP_ISS, OPTIONS, 19, 4) +FIELD(MOP_ISS, IS_SETG, 23, 1) +FIELD(MOP_ISS, MEMINST, 24, 1) + static inline uint32_t syn_mop(bool is_set, bool is_setg, int options, bool epilogue, bool wrong_option, bool option_a, int destreg, int srcreg, int sizereg) { - return (EC_MOP << ARM_EL_EC_SHIFT) | ARM_EL_IL | - (is_set << 24) | (is_setg << 23) | (options << 19) | - (epilogue << 18) | (wrong_option << 17) | (option_a << 16) | - (destreg << 10) | (srcreg << 5) | sizereg; + uint32_t res = syn_set_ec(0, EC_MOP); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, MOP_ISS, MEMINST, is_set); + res = FIELD_DP32(res, MOP_ISS, IS_SETG, is_setg); + res = FIELD_DP32(res, MOP_ISS, OPTIONS, options); + res = FIELD_DP32(res, MOP_ISS, EPILOGUE, epilogue); + res = FIELD_DP32(res, MOP_ISS, WRONG_OPT, wrong_option); + res = FIELD_DP32(res, MOP_ISS, OPT_A, option_a); + res = FIELD_DP32(res, MOP_ISS, DESTREG, destreg); + res = FIELD_DP32(res, MOP_ISS, SRCREG, srcreg); + res = FIELD_DP32(res, MOP_ISS, SIZEREG, sizereg); + + return res; } -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> It shares the same COPROC_ISS encoding as the other CP traps although not all the fields are used. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-16-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 16 ++++++++++++++++ target/arm/tcg/vfp_helper.c | 5 +---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ FIELD(COPROC_ISS, OP2, 17, 3) FIELD(COPROC_ISS, COND, 20, 4) FIELD(COPROC_ISS, CV, 24, 1) +static inline uint32_t syn_cp10_rt_trap(int cv, int cond, int opc1, + int crn, int rt, int isread) +{ + uint32_t res = syn_set_ec(0, EC_FPIDTRAP); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, COPROC_ISS, CV, cv); + res = FIELD_DP32(res, COPROC_ISS, COND, cond); + res = FIELD_DP32(res, COPROC_ISS, OP1, opc1); + res = FIELD_DP32(res, COPROC_ISS, CRN, crn); + res = FIELD_DP32(res, COPROC_ISS, RT, rt); + res = FIELD_DP32(res, COPROC_ISS, ISREAD, isread); + + return res; +} + static inline uint32_t syn_cp14_rt_trap(int cv, int cond, int opc1, int opc2, int crn, int crm, int rt, int isread, bool is_16bit) diff --git a/target/arm/tcg/vfp_helper.c b/target/arm/tcg/vfp_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/vfp_helper.c +++ b/target/arm/tcg/vfp_helper.c @@ -XXX,XX +XXX,XX @@ void HELPER(check_hcr_el2_trap)(CPUARMState *env, uint32_t rt, uint32_t reg) g_assert_not_reached(); } - syndrome = ((EC_FPIDTRAP << ARM_EL_EC_SHIFT) - | ARM_EL_IL - | (1 << 24) | (0xe << 20) | (7 << 14) - | (reg << 10) | (rt << 5) | 1); + syndrome = syn_cp10_rt_trap(1, 0xe, 7, reg, rt, 1); raise_exception(env, EXCP_HYP_TRAP, syndrome, 2); } -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> One more step towards dropping the old #defines. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-17-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void arm_cpu_do_interrupt_aarch32_hyp(CPUState *cs) */ if (cs->exception_index == EXCP_PREFETCH_ABORT || (cs->exception_index == EXCP_DATA_ABORT && - !(env->exception.syndrome & ARM_EL_ISV)) || + !FIELD_EX32(env->exception.syndrome, SYNDROME, IL)) || syn_get_ec(env->exception.syndrome) == EC_UNCATEGORIZED) { - env->exception.syndrome &= ~ARM_EL_IL; + env->exception.syndrome = FIELD_DP32(env->exception.syndrome, SYNDROME, IL, 0); } } env->cp15.esr_el[2] = env->exception.syndrome; -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> This removes the last use of ARM_EL_EC_SHIFT. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-18-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 1 - target/arm/tcg/debug.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ typedef enum { } GCSInstructionType; #define ARM_EL_EC_LENGTH 6 -#define ARM_EL_EC_SHIFT 26 #define ARM_EL_IL_SHIFT 25 #define ARM_EL_ISV_SHIFT 24 #define ARM_EL_IL (1 << ARM_EL_IL_SHIFT) diff --git a/target/arm/tcg/debug.c b/target/arm/tcg/debug.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/debug.c +++ b/target/arm/tcg/debug.c @@ -XXX,XX +XXX,XX @@ raise_exception_debug(CPUARMState *env, uint32_t excp, uint32_t syndrome) * Similarly for watchpoint and breakpoint matches. */ assert(debug_el >= cur_el); - syndrome |= (debug_el == cur_el) << ARM_EL_EC_SHIFT; + syndrome |= (debug_el == cur_el) << R_SYNDROME_EC_SHIFT; raise_exception(env, excp, syndrome, debug_el); } -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Rather than open coding a bunch of shifts and masks we can use the syndrome definitions. While we are at it assert it really is a EC_DATAABORT. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-19-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/whpx/whpx-all.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/target/arm/whpx/whpx-all.c b/target/arm/whpx/whpx-all.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/whpx/whpx-all.c +++ b/target/arm/whpx/whpx-all.c @@ -XXX,XX +XXX,XX @@ static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx) { uint64_t syndrome = ctx->Syndrome; - bool isv = syndrome & ARM_EL_ISV; - bool iswrite = (syndrome >> 6) & 1; - bool sse = (syndrome >> 21) & 1; - uint32_t sas = (syndrome >> 22) & 3; + bool isv = FIELD_EX32(syndrome, DABORT_ISS, ISV); + bool iswrite = FIELD_EX32(syndrome, DABORT_ISS, WNR); + bool sse = FIELD_EX32(syndrome, DABORT_ISS, SSE); + uint32_t sas = FIELD_EX32(syndrome, DABORT_ISS, SAS); uint32_t len = 1 << sas; - uint32_t srt = (syndrome >> 16) & 0x1f; - uint32_t cm = (syndrome >> 8) & 0x1; + uint32_t srt = FIELD_EX32(syndrome, DABORT_ISS, SRT); + uint32_t cm = FIELD_EX32(syndrome, DABORT_ISS, CM); uint64_t val = 0; + assert(syn_get_ec(syndrome) == EC_DATAABORT); assert(!cm); assert(isv); -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Rather than open coding a bunch of shifts and masks we can use the syndrome definitions. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260422125250.1303100-20-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -XXX,XX +XXX,XX @@ static int hvf_handle_exception(CPUState *cpu, hv_vcpu_exit_exception_t *excp) break; } case EC_DATAABORT: { - bool isv = syndrome & ARM_EL_ISV; - bool iswrite = (syndrome >> 6) & 1; - bool s1ptw = (syndrome >> 7) & 1; - bool sse = (syndrome >> 21) & 1; - uint32_t sas = (syndrome >> 22) & 3; + bool isv = FIELD_EX32(syndrome, DABORT_ISS, ISV); + bool iswrite = FIELD_EX32(syndrome, DABORT_ISS, WNR); + bool s1ptw = FIELD_EX32(syndrome, DABORT_ISS, S1PTW); + bool sse = FIELD_EX32(syndrome, DABORT_ISS, SSE); + uint32_t sas = FIELD_EX32(syndrome, DABORT_ISS, SAS); uint32_t len = 1 << sas; - uint32_t srt = (syndrome >> 16) & 0x1f; - uint32_t cm = (syndrome >> 8) & 0x1; + uint32_t srt = FIELD_EX32(syndrome, DABORT_ISS, SRT); + uint32_t cm = FIELD_EX32(syndrome, DABORT_ISS, CM); uint64_t val = 0; uint64_t ipa = excp->physical_address; AddressSpace *as = cpu_get_address_space(cpu, ARMASIdx_NS); -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> One more step to removing the old defines. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-21-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/tlb_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/tlb_helper.c b/target/arm/tcg/tlb_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/tlb_helper.c +++ b/target/arm/tcg/tlb_helper.c @@ -XXX,XX +XXX,XX @@ static inline uint64_t merge_syn_data_abort(uint32_t template_syn, */ assert(!fi->stage2); syn = syn_data_abort_vncr(fi->ea, is_write, fsc); - } else if (!(template_syn & ARM_EL_ISV) || target_el != 2 + } else if (!FIELD_EX32(template_syn, DABORT_ISS, ISV) || target_el != 2 || fi->s1ptw || !fi->stage2) { syn = syn_data_abort_no_iss(same_el, 0, fi->ea, 0, fi->s1ptw, is_write, fsc); -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> These are only valid for data abort syndromes. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-22-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/tlb_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/tlb_helper.c b/target/arm/tcg/tlb_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/tlb_helper.c +++ b/target/arm/tcg/tlb_helper.c @@ -XXX,XX +XXX,XX @@ static inline uint64_t merge_syn_data_abort(uint32_t template_syn, * ST64BV, or ST64BV0 insns report syndrome info even for stage-1 * faults and regardless of the target EL. */ - if (template_syn & ARM_EL_VNCR) { + if (FIELD_EX32(template_syn, DABORT_ISS, VNCR)) { /* * FEAT_NV2 faults on accesses via VNCR_EL2 are a special case: * they are always reported as "same EL", even though we are going @@ -XXX,XX +XXX,XX @@ void arm_deliver_fault(ARMCPU *cpu, vaddr addr, * because we masked that out in disas_set_insn_syndrome()) */ bool is_vncr = (access_type != MMU_INST_FETCH) && - (env->exception.syndrome & ARM_EL_VNCR); + FIELD_EX32(env->exception.syndrome, DABORT_ISS, VNCR); if (is_vncr) { /* FEAT_NV2 faults on accesses via VNCR_EL2 go to EL2 */ -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Now everything is defined with registerfields we can drop the old defines. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-23-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ typedef enum { GCS_IT_GCSPOPX = 9, } GCSInstructionType; -#define ARM_EL_EC_LENGTH 6 -#define ARM_EL_IL_SHIFT 25 -#define ARM_EL_ISV_SHIFT 24 -#define ARM_EL_IL (1 << ARM_EL_IL_SHIFT) -#define ARM_EL_ISV (1 << ARM_EL_ISV_SHIFT) - -/* In the Data Abort syndrome */ -#define ARM_EL_VNCR (1 << 13) - static inline uint32_t syn_get_ec(uint32_t syn) { return FIELD_EX32(syn, SYNDROME, EC); -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Pass the register number (rd) to the wfit helper and report it in the syndrome ISS. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-24-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 11 ++++++++++- target/arm/tcg/helper-defs.h | 2 +- target/arm/tcg/op_helper.c | 7 ++++--- target/arm/tcg/translate-a64.c | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ FIELD(WFX_ISS, RN, 5, 5) FIELD(WFX_ISS, COND, 20, 4) FIELD(WFX_ISS, CV, 24, 1) -static inline uint32_t syn_wfx(int cv, int cond, int ti, bool is_16bit) +typedef enum { + WFI = 0b00, + WFE = 0b01, + WFIT = 0b10, + WFET = 0xb11 +} wfx_ti; + +static inline uint32_t syn_wfx(int cv, int cond, int rn, bool rv, wfx_ti ti, bool is_16bit) { uint32_t res = syn_set_ec(0, EC_WFX_TRAP); res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_wfx(int cv, int cond, int ti, bool is_16bit) res = FIELD_DP32(res, WFX_ISS, CV, cv); res = FIELD_DP32(res, WFX_ISS, COND, cond); res = FIELD_DP32(res, WFX_ISS, TI, ti); + res = FIELD_DP32(res, WFX_ISS, RN, rn); + res = FIELD_DP32(res, WFX_ISS, RV, rv); return res; } diff --git a/target/arm/tcg/helper-defs.h b/target/arm/tcg/helper-defs.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/helper-defs.h +++ b/target/arm/tcg/helper-defs.h @@ -XXX,XX +XXX,XX @@ DEF_HELPER_2(exception_pc_alignment, noreturn, env, vaddr) DEF_HELPER_1(setend, void, env) DEF_HELPER_2(wfi, void, env, i32) DEF_HELPER_1(wfe, void, env) -DEF_HELPER_2(wfit, void, env, i64) +DEF_HELPER_2(wfit, void, env, i32) DEF_HELPER_1(yield, void, env) DEF_HELPER_1(pre_hvc, void, env) DEF_HELPER_2(pre_smc, void, env, i32) diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -XXX,XX +XXX,XX @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len) env->regs[15] -= insn_len; } - raise_exception(env, excp, syn_wfx(1, 0xe, 0, insn_len == 2), + raise_exception(env, excp, syn_wfx(1, 0xe, 0, false, WFI, insn_len == 2), target_el); } @@ -XXX,XX +XXX,XX @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len) #endif } -void HELPER(wfit)(CPUARMState *env, uint64_t timeout) +void HELPER(wfit)(CPUARMState *env, uint32_t rd) { #ifdef CONFIG_USER_ONLY /* @@ -XXX,XX +XXX,XX @@ void HELPER(wfit)(CPUARMState *env, uint64_t timeout) int target_el = check_wfx_trap(env, false, &excp); /* The WFIT should time out when CNTVCT_EL0 >= the specified value. */ uint64_t cntval = gt_get_countervalue(env); + uint64_t timeout = env->xregs[rd]; /* * We want the value that we would get if we read CNTVCT_EL0 from * the current exception level, so the direct_access offset, not @@ -XXX,XX +XXX,XX @@ void HELPER(wfit)(CPUARMState *env, uint64_t timeout) if (target_el) { env->pc -= 4; - raise_exception(env, excp, syn_wfx(1, 0xe, 2, false), target_el); + raise_exception(env, excp, syn_wfx(1, 0xe, rd, true, WFIT, false), target_el); } if (uadd64_overflow(timeout, offset, &nexttick)) { diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -XXX,XX +XXX,XX @@ static bool trans_WFIT(DisasContext *s, arg_WFIT *a) } gen_a64_update_pc(s, 4); - gen_helper_wfit(tcg_env, cpu_reg(s, a->rd)); + gen_helper_wfit(tcg_env, tcg_constant_i32(a->rd)); /* Go back to the main loop to check for interrupts */ s->base.is_jmp = DISAS_EXIT; return true; -- 2.43.0