On 10/31/25 11:50 AM, Shameer Kolothum wrote:
> QEMU SMMUv3 does not enable ATS (Address Translation Services) by default.
> When accelerated mode is enabled and the host SMMUv3 supports ATS, it can
> be useful to report ATS capability to the guest so it can take advantage
> of it if the device also supports ATS.
>
> Note: ATS support cannot be reliably detected from the host SMMUv3 IDR
> registers alone, as firmware ACPI IORT tables may override them. The
> user must therefore ensure the support before enabling it.
Please add a note that this is a partial ATS support, made possible because
emulated devices can be pluugged onto accel SMMU (ie. we do not support
ATS translation requests for instance)
>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Tested-by: Zhangfei Gao <zhangfei.gao@linaro.org>
> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com>
> ---
> hw/arm/smmuv3-accel.c | 4 ++++
> hw/arm/smmuv3.c | 25 ++++++++++++++++++++++++-
> hw/arm/virt-acpi-build.c | 10 ++++++++--
> include/hw/arm/smmuv3.h | 1 +
> 4 files changed, 37 insertions(+), 3 deletions(-)
>
> diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c
> index 35298350cb..5b0ef3804a 100644
> --- a/hw/arm/smmuv3-accel.c
> +++ b/hw/arm/smmuv3-accel.c
> @@ -645,6 +645,10 @@ void smmuv3_accel_idr_override(SMMUv3State *s)
> if (!s->ril) {
> s->idr[3] = FIELD_DP32(s->idr[3], IDR3, RIL, 0);
> }
> + /* QEMU SMMUv3 has no ATS. Update IDR0 if user has enabled it */
"advertise ats if opt-on by property?"
> + if (s->ats) {
> + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, ATS, 1); /* ATS */
use s->ats directly?
> + }
> }
>
> /* Based on SMUUv3 GBPA configuration, attach a corresponding HWPT */
> diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
> index b9d96f5762..d95279a733 100644
> --- a/hw/arm/smmuv3.c
> +++ b/hw/arm/smmuv3.c
> @@ -1517,13 +1517,28 @@ static int smmuv3_cmdq_consume(SMMUv3State *s)
> */
> smmuv3_range_inval(bs, &cmd, SMMU_STAGE_2);
> break;
> + case SMMU_CMD_ATC_INV:
> + {
> + SMMUDevice *sdev = smmu_find_sdev(bs, CMD_SID(&cmd));
> + Error *local_err = NULL;
> +
> + if (!sdev) {
> + break;
> + }
> +
> + if (!smmuv3_accel_issue_inv_cmd(s, &cmd, sdev, &local_err)) {
> + error_report_err(local_err);
> + cmd_error = SMMU_CERROR_ILL;
> + break;
> + }
> + break;
> + }
> case SMMU_CMD_TLBI_EL3_ALL:
> case SMMU_CMD_TLBI_EL3_VA:
> case SMMU_CMD_TLBI_EL2_ALL:
> case SMMU_CMD_TLBI_EL2_ASID:
> case SMMU_CMD_TLBI_EL2_VA:
> case SMMU_CMD_TLBI_EL2_VAA:
> - case SMMU_CMD_ATC_INV:
> case SMMU_CMD_PRI_RESP:
> case SMMU_CMD_RESUME:
> case SMMU_CMD_STALL_TERM:
> @@ -1942,6 +1957,10 @@ static bool smmu_validate_property(SMMUv3State *s, Error **errp)
> error_setg(errp, "ril can only be disabled if accel=on");
> return false;
> }
> + if (s->ats) {
> + error_setg(errp, "ats can only be enabled if accel=on");
> + return false;
> + }
> return false;
> }
> return true;
> @@ -2067,6 +2086,7 @@ static const Property smmuv3_properties[] = {
> DEFINE_PROP_BOOL("accel", SMMUv3State, accel, false),
> /* RIL can be turned off for accel cases */
> DEFINE_PROP_BOOL("ril", SMMUv3State, ril, true),
> + DEFINE_PROP_BOOL("ats", SMMUv3State, ats, false),
> };
>
> static void smmuv3_instance_init(Object *obj)
> @@ -2096,6 +2116,9 @@ static void smmuv3_class_init(ObjectClass *klass, const void *data)
> "in nested mode for vfio-pci dev assignment");
> object_class_property_set_description(klass, "ril",
> "Disable range invalidation support (for accel=on)");
> + object_class_property_set_description(klass, "ats",
> + "Enable/disable ATS support (for accel=on). Please ensure host "
> + "platform has ATS support before enabling this");
> }
>
> static int smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu,
> diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
> index 6106ad1b6e..1b0d0a2029 100644
> --- a/hw/arm/virt-acpi-build.c
> +++ b/hw/arm/virt-acpi-build.c
> @@ -345,6 +345,7 @@ typedef struct AcpiIortSMMUv3Dev {
> /* Offset of the SMMUv3 IORT Node relative to the start of the IORT */
> size_t offset;
> bool accel;
> + bool ats;
> } AcpiIortSMMUv3Dev;
>
> /*
> @@ -400,6 +401,7 @@ static int iort_smmuv3_devices(Object *obj, void *opaque)
>
> bus = PCI_BUS(object_property_get_link(obj, "primary-bus", &error_abort));
> sdev.accel = object_property_get_bool(obj, "accel", &error_abort);
> + sdev.ats = object_property_get_bool(obj, "ats", &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);
> @@ -544,6 +546,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
> int i, nb_nodes, rc_mapping_count;
> AcpiIortSMMUv3Dev *sdev;
> size_t node_size;
> + bool ats_needed = false;
> int num_smmus = 0;
> uint32_t id = 0;
> int rc_smmu_idmaps_len = 0;
> @@ -579,6 +582,9 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
> /* Calculate RMR nodes required. One per SMMUv3 with accelerated mode */
> for (i = 0; i < num_smmus; i++) {
> sdev = &g_array_index(smmuv3_devs, AcpiIortSMMUv3Dev, i);
> + if (sdev->ats) {
> + ats_needed = true;
> + }
> if (sdev->accel) {
> nb_nodes++;
> }
> @@ -678,8 +684,8 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
> build_append_int_noprefix(table_data, 0, 2); /* Reserved */
> /* Table 15 Memory Access Flags */
> build_append_int_noprefix(table_data, 0x3 /* CCA = CPM = DACS = 1 */, 1);
> -
> - build_append_int_noprefix(table_data, 0, 4); /* ATS Attribute */
> + /* ATS Attribute */
> + build_append_int_noprefix(table_data, (ats_needed ? 1 : 0), 4);
can't you use ats_needed directly?
> /* MCFG pci_segment */
> build_append_int_noprefix(table_data, 0, 4); /* PCI Segment number */
>
> diff --git a/include/hw/arm/smmuv3.h b/include/hw/arm/smmuv3.h
> index 95202c2757..5fd5ec7b49 100644
> --- a/include/hw/arm/smmuv3.h
> +++ b/include/hw/arm/smmuv3.h
> @@ -69,6 +69,7 @@ struct SMMUv3State {
> struct SMMUv3AccelState *s_accel;
> Error *migration_blocker;
> bool ril;
> + bool ats;
> };
>
> typedef enum {
Eric