[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v4 07/22] xen/arm: smmuv3: Extend HW features detection and apply errata


  • To: "xen-devel@xxxxxxxxxxxxxxxxxxxx" <xen-devel@xxxxxxxxxxxxxxxxxxxx>
  • From: Milan Djokic <milan_djokic@xxxxxxxx>
  • Date: Wed, 7 Oct 2026 00:06:54 +0000
  • Accept-language: en-US
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=epam.com; dmarc=pass action=none header.from=epam.com; dkim=pass header.d=epam.com; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=Z7l/InagsXbj6Ej1ONUjrCevKepncUUfCTiVi69NQnI=; b=c7oK6wgM362g9PBpsTEG9QzTOqIoYvt5nvUBEExx1BXedGX06gnBkOC0WDZmLE7ho6+LqvtZiuG0J5rqlqxeV2noFkIQKuHm0m//qL5ra1IgMga3nEIrxDTafM/HsLYWAel/uUVDrf3vspNYDgAPB7w2qzhYyXpLz08hYmyhlssVFDc16SYh2XEWqFsLOeGmw8s7c0gHhTphvp1+AiHpXoPNGFY5HGInvVUo/ai85cDHmzTJZ2HKc5OKO/qqPZTyXdowO8R5lUEbvBvvGLZ5t4cRowZtZD0rYULvJH6QOYFT+imRMV1cfN7ll5IdTTrFtoYt2L0Nghjr9tL5aqfNnA==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=Kqrhb0Xs8F4S5hJLMV6zn+1Qa7CE6KcuoRWRQtsgNX30RizYbjc20T108PkqSTmuK1htmrhAgVIuEnSoWpIYdDweZWejK03WImcxNqJEFuebeHKC5QsDez6Xg/9CZwySsgF/yOMuQUp7iIA31v+3J5L0zNdIWjajhRXRN4a5T2I+OodPUI7p0rVHM7SVLm5/oTSjHKC/STDZsGZZwEL6Tuwr9qx0QlALlaqYmkLiMOcDfZgcaILA7HN89ESHkO4onxgcmComuHWps3gsojQ8lcc5sh06xMrDNlrjy1AhW1+NHM/51OZmb0UX6FdOE8Rjik+FbEs6ZBlrT6a7ae3bOg==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=selector1 header.d=epam.com header.i="@epam.com" header.h="From:Date:Subject:Message-ID:Content-Type:MIME-Version:x-ms-exchange-senderadcheck"
  • Authentication-results: mx.microsoft.com 1; dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=epam.com;
  • Cc: Milan Djokic <milan_djokic@xxxxxxxx>, Stefano Stabellini <sstabellini@xxxxxxxxxx>, Julien Grall <julien@xxxxxxx>, Bertrand Marquis <bertrand.marquis@xxxxxxx>, Michal Orzel <michal.orzel@xxxxxxx>, Volodymyr Babchuk <Volodymyr_Babchuk@xxxxxxxx>, Rahul Singh <rahul.singh@xxxxxxx>, Robin Murphy <robin.murphy@xxxxxxx>, Will Deacon <will@xxxxxxxxxx>
  • Delivery-date: Wed, 07 Oct 2026 00:06:59 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>
  • Thread-index: AQHdVe/CHqFUnj9RrUenBNlAdTO4Ww==
  • Thread-topic: [PATCH v4 07/22] xen/arm: smmuv3: Extend HW features detection and apply errata

Backport Linux commits 4bbd63eb685b and fd86b5944215, adapted to the
Xen SMMUv3 implementation, applying errata workarounds to prevent
nesting on MMU-600 and MMU-700 products.

Detect IOMMU hardware features and store them in the IOMMU host list
for each probed SMMUv3 device, allowing the emulated IOMMU to advertise
features supported by the physical IOMMU.

Linux commits:

  4bbd63eb685b ("iommu/arm-smmu-v3: Work around MMU-600 erratum 1076982")
  fd86b5944215 ("iommu/arm-smmu-v3: Add explicit feature for nesting")

Link: 
https://lore.kernel.org/all/08adbe3d01024d8382a478325f73b56851f76e49.1683731256.git.robin.murphy@xxxxxxx/
Link: 
https://lore.kernel.org/all/136c3f4a3a84cc14a5a1978ace57dfd3ed67b688.1683731256.git.robin.murphy@xxxxxxx/
Origin: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 
4bbd63eb685b
Origin: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 
fd86b5944215

Signed-off-by: Robin Murphy <robin.murphy@xxxxxxx>
Signed-off-by: Will Deacon <will@xxxxxxxxxx>
Signed-off-by: Rahul Singh <rahul.singh@xxxxxxx>
Signed-off-by: Milan Djokic <milan_djokic@xxxxxxxx>
---
 xen/arch/arm/include/asm/viommu.h      |  8 ++-
 xen/drivers/passthrough/arm/smmu-v3.c  | 81 ++++++++++++++++++++++++--
 xen/drivers/passthrough/arm/smmu-v3.h  | 32 ++++++++--
 xen/drivers/passthrough/arm/viommu.c   |  4 +-
 xen/drivers/passthrough/arm/vsmmu-v3.c | 44 ++++++++++++--
 5 files changed, 152 insertions(+), 17 deletions(-)

diff --git a/xen/arch/arm/include/asm/viommu.h 
b/xen/arch/arm/include/asm/viommu.h
index 287595068b..844cf34d56 100644
--- a/xen/arch/arm/include/asm/viommu.h
+++ b/xen/arch/arm/include/asm/viommu.h
@@ -12,6 +12,8 @@ struct host_iommu {
     struct list_head entry;
     /* IOMMU DT node used to establish virtual to physical IOMMU/SID mapping */
     const struct dt_device_node *dt_node;
+    /* Physical IOMMU device structure used to detect supported features */
+    const void *iommu;
     /* Used for dom0 vIOMMU instantiation (1-1 MMIO regions mappings) */
     paddr_t addr;
     /* Used for dom0 vIOMMU instantiation (1-1 MMIO regions mappings) */
@@ -52,7 +54,8 @@ int domain_viommu_init(struct domain *d, uint8_t viommu_type);
 int viommu_relinquish_resources(struct domain *d);
 uint8_t viommu_get_type(void);
 void add_to_host_iommu_list(paddr_t addr, paddr_t size,
-                            const struct dt_device_node *node);
+                            const struct dt_device_node *node,
+                            const void *iommu);
 void set_cur_viommu(const struct viommu_desc *desc);
 unsigned int domain_viommu_get_num_mmio_handlers(struct domain *d);
 
@@ -77,7 +80,8 @@ static inline int viommu_relinquish_resources(struct domain 
*d)
 }
 
 static inline void __init add_to_host_iommu_list(paddr_t addr, paddr_t size,
-                                          const struct dt_device_node *node)
+                                          const struct dt_device_node *node,
+                                          const void *iommu)
 {
     return;
 }
diff --git a/xen/drivers/passthrough/arm/smmu-v3.c 
b/xen/drivers/passthrough/arm/smmu-v3.c
index 21b82eba85..9d2b8a708b 100644
--- a/xen/drivers/passthrough/arm/smmu-v3.c
+++ b/xen/drivers/passthrough/arm/smmu-v3.c
@@ -2451,6 +2451,37 @@ err_free_irqs:
        return ret;
 }
 
+static void arm_smmu_device_iidr_probe(struct arm_smmu_device *smmu)
+{
+       u32 reg;
+       unsigned int implementer, productid, variant, revision;
+
+       reg = readl_relaxed(smmu->base + ARM_SMMU_IIDR);
+       implementer = FIELD_GET(IIDR_IMPLEMENTER, reg);
+       productid = FIELD_GET(IIDR_PRODUCTID, reg);
+       variant = FIELD_GET(IIDR_VARIANT, reg);
+       revision = FIELD_GET(IIDR_REVISION, reg);
+
+       switch (implementer) {
+       case IIDR_IMPLEMENTER_ARM:
+               switch (productid) {
+               case IIDR_PRODUCTID_ARM_MMU_600:
+                       /* Arm erratum 1076982 */
+                       if (variant == 0 && revision <= 2)
+                               smmu->features &= ~ARM_SMMU_FEAT_SEV;
+                       /* Arm erratum 1209401 */
+                       if (variant < 2)
+                               smmu->features &= ~ARM_SMMU_FEAT_NESTING;
+                       break;
+               case IIDR_PRODUCTID_ARM_MMU_700:
+                       /* Arm errata 2268618, 2812531 */
+                       smmu->features &= ~ARM_SMMU_FEAT_NESTING;
+                       break;
+               }
+               break;
+       }
+}
+
 static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
 {
        u32 reg;
@@ -2539,8 +2570,10 @@ static int arm_smmu_device_hw_probe(struct 
arm_smmu_device *smmu)
        switch (FIELD_GET(IDR0_TTF, reg)) {
        case IDR0_TTF_AARCH32_64:
                smmu->ias = 40;
+               smmu->features |= ARM_SMMU_FEAT_TTF_AARCH32_64;
                fallthrough;
        case IDR0_TTF_AARCH64:
+               smmu->features |= ARM_SMMU_FEAT_TTF_AARCH64;
                break;
        default:
                dev_err(smmu->dev, "AArch64 table format not supported!\n");
@@ -2549,6 +2582,8 @@ static int arm_smmu_device_hw_probe(struct 
arm_smmu_device *smmu)
 
        /* ASID/VMID sizes */
        smmu->vmid_bits = reg & IDR0_VMID16 ? 16 : 8;
+       if (reg & IDR0_ASID16)
+               smmu->features |= ARM_SMMU_FEAT_ASID_16;
 
        /* IDR1 */
        reg = readl_relaxed(smmu->base + ARM_SMMU_IDR1);
@@ -2588,12 +2623,18 @@ static int arm_smmu_device_hw_probe(struct 
arm_smmu_device *smmu)
        smmu->evtq.max_stalls = FIELD_GET(IDR5_STALL_MAX, reg);
 
        /* Page sizes */
-       if (reg & IDR5_GRAN64K)
+       if (reg & IDR5_GRAN64K) {
                smmu->pgsize_bitmap |= SZ_64K | SZ_512M;
-       if (reg & IDR5_GRAN16K)
+               smmu->features |= ARM_SMMU_FEAT_GRAN64K;
+       }
+       if (reg & IDR5_GRAN16K) {
                smmu->pgsize_bitmap |= SZ_16K | SZ_32M;
-       if (reg & IDR5_GRAN4K)
+               smmu->features |= ARM_SMMU_FEAT_GRAN16K;
+       }
+       if (reg & IDR5_GRAN4K) {
                smmu->pgsize_bitmap |= SZ_4K | SZ_2M | SZ_1G;
+               smmu->features |= ARM_SMMU_FEAT_GRAN4K;
+       }
 
        /* Input address size */
        if (FIELD_GET(IDR5_VAX, reg) == IDR5_VAX_52_BIT)
@@ -2626,15 +2667,46 @@ static int arm_smmu_device_hw_probe(struct 
arm_smmu_device *smmu)
                fallthrough;
        case IDR5_OAS_48_BIT:
                smmu->oas = 48;
+               smmu->features |= ARM_SMMU_FEAT_OAS_48_BIT;
                break;
        }
 
        smmu->oas = min_t(unsigned long, PADDR_BITS, smmu->oas);
        smmu->ias = max(smmu->ias, smmu->oas);
 
+       switch (smmu->oas) {
+       case 32:
+               smmu->features |= ARM_SMMU_FEAT_OAS_32_BIT;
+               break;
+       case 36:
+               smmu->features |= ARM_SMMU_FEAT_OAS_36_BIT;
+               break;
+       case 40:
+               smmu->features |= ARM_SMMU_FEAT_OAS_40_BIT;
+               break;
+       case 42:
+               smmu->features |= ARM_SMMU_FEAT_OAS_42_BIT;
+               break;
+       case 44:
+               smmu->features |= ARM_SMMU_FEAT_OAS_44_BIT;
+               break;
+       case 48:
+               smmu->features |= ARM_SMMU_FEAT_OAS_48_BIT;
+               break;
+       case 52:
+               smmu->features |= ARM_SMMU_FEAT_OAS_52_BIT;
+               break;
+       }
+
        /* Xen: Set maximum Stage-2 input size supported by the SMMU. */
        p2m_restrict_ipa_bits(smmu->ias);
 
+       if ((smmu->features & ARM_SMMU_FEAT_TRANS_S1) &&
+               (smmu->features & ARM_SMMU_FEAT_TRANS_S2))
+               smmu->features |= ARM_SMMU_FEAT_NESTING;
+
+       arm_smmu_device_iidr_probe(smmu);
+
        dev_info(smmu->dev, "ias %lu-bit, oas %lu-bit (features 0x%08x)\n",
                 smmu->ias, smmu->oas, smmu->features);
        return 0;
@@ -2829,9 +2901,8 @@ static int __init arm_smmu_device_probe(struct 
platform_device *pdev)
        spin_lock(&arm_smmu_devices_lock);
        list_add(&smmu->devices, &arm_smmu_devices);
        spin_unlock(&arm_smmu_devices_lock);
-
        /* Add to host IOMMU list to initialize vIOMMU for dom0 */
-       add_to_host_iommu_list(ioaddr, iosize, dev_to_dt(pdev));
+       add_to_host_iommu_list(ioaddr, iosize, dev_to_dt(pdev), (void *) smmu);
 
        return 0;
 
diff --git a/xen/drivers/passthrough/arm/smmu-v3.h 
b/xen/drivers/passthrough/arm/smmu-v3.h
index e257de6db9..ef492098ae 100644
--- a/xen/drivers/passthrough/arm/smmu-v3.h
+++ b/xen/drivers/passthrough/arm/smmu-v3.h
@@ -60,6 +60,16 @@
 #define IDR5_VAX                       GENMASK(11, 10)
 #define IDR5_VAX_52_BIT                        1
 
+#define ARM_SMMU_IIDR                  0x18
+#define IIDR_PRODUCTID                 GENMASK(31, 20)
+#define IIDR_VARIANT                   GENMASK(19, 16)
+#define IIDR_REVISION                  GENMASK(15, 12)
+#define IIDR_IMPLEMENTER               GENMASK(11, 0)
+
+#define IIDR_IMPLEMENTER_ARM           0x43b
+#define IIDR_PRODUCTID_ARM_MMU_600     0x483
+#define IIDR_PRODUCTID_ARM_MMU_700     0x487
+
 #define ARM_SMMU_CR0                   0x20
 #define CR0_ATSCHK                     (1 << 4)
 #define CR0_CMDQEN                     (1 << 3)
@@ -602,10 +612,24 @@ struct arm_smmu_device {
 #define ARM_SMMU_FEAT_COHERENCY                (1 << 8)
 #define ARM_SMMU_FEAT_TRANS_S1         (1 << 9)
 #define ARM_SMMU_FEAT_TRANS_S2         (1 << 10)
-#define ARM_SMMU_FEAT_STALLS           (1 << 11)
-#define ARM_SMMU_FEAT_HYP              (1 << 12)
-#define ARM_SMMU_FEAT_STALL_FORCE      (1 << 13)
-#define ARM_SMMU_FEAT_VAX              (1 << 14)
+#define ARM_SMMU_FEAT_NESTING      (1 << 11)
+#define ARM_SMMU_FEAT_STALLS           (1 << 12)
+#define ARM_SMMU_FEAT_HYP              (1 << 13)
+#define ARM_SMMU_FEAT_STALL_FORCE      (1 << 14)
+#define ARM_SMMU_FEAT_VAX              (1 << 15)
+#define ARM_SMMU_FEAT_ASID_16          (1 << 16)
+#define ARM_SMMU_FEAT_TTF_AARCH64              (1 << 17)
+#define ARM_SMMU_FEAT_TTF_AARCH32_64   (1 << 18)
+#define ARM_SMMU_FEAT_GRAN64K  (1 << 19)
+#define ARM_SMMU_FEAT_GRAN16K  (1 << 20)
+#define ARM_SMMU_FEAT_GRAN4K   (1 << 21)
+#define ARM_SMMU_FEAT_OAS_32_BIT       (1 << 22)
+#define ARM_SMMU_FEAT_OAS_36_BIT       (1 << 23)
+#define ARM_SMMU_FEAT_OAS_40_BIT       (1 << 24)
+#define ARM_SMMU_FEAT_OAS_42_BIT       (1 << 25)
+#define ARM_SMMU_FEAT_OAS_44_BIT       (1 << 26)
+#define ARM_SMMU_FEAT_OAS_48_BIT       (1 << 27)
+#define ARM_SMMU_FEAT_OAS_52_BIT       (1 << 28)
        u32                             features;
 
 #define ARM_SMMU_OPT_SKIP_PREFETCH     (1 << 0)
diff --git a/xen/drivers/passthrough/arm/viommu.c 
b/xen/drivers/passthrough/arm/viommu.c
index 3401e4f7d9..407e68e62d 100644
--- a/xen/drivers/passthrough/arm/viommu.c
+++ b/xen/drivers/passthrough/arm/viommu.c
@@ -17,7 +17,8 @@ static bool host_iommu_list_incomplete;
 
 /* Common function for adding to host_iommu_list */
 void __init add_to_host_iommu_list(paddr_t addr, paddr_t size,
-                            const struct dt_device_node *node)
+                            const struct dt_device_node *node,
+                            const void *iommu)
 {
     struct host_iommu *iommu_data;
     int irq;
@@ -45,6 +46,7 @@ void __init add_to_host_iommu_list(paddr_t addr, paddr_t size,
         return;
     }
     iommu_data->irq = irq;
+    iommu_data->iommu = iommu;
 
     printk("vIOMMU: Found IOMMU @0x%"PRIx64"\n", addr);
 
diff --git a/xen/drivers/passthrough/arm/vsmmu-v3.c 
b/xen/drivers/passthrough/arm/vsmmu-v3.c
index ea3b5bb34d..7001f8c23f 100644
--- a/xen/drivers/passthrough/arm/vsmmu-v3.c
+++ b/xen/drivers/passthrough/arm/vsmmu-v3.c
@@ -5,9 +5,13 @@
 #include <asm/mmio.h>
 #include <asm/viommu.h>
 
+#include "smmu-v3.h"
+#include "vsmmu-v3.h"
+
 struct virt_smmu {
     struct      domain *d;
     struct      list_head viommu_list;
+    uint32_t    features;
 };
 
 static int vsmmuv3_mmio_write(struct vcpu *v, mmio_info_t *info,
@@ -29,7 +33,8 @@ static const struct mmio_handler_ops vsmmuv3_mmio_handler = {
     .write = vsmmuv3_mmio_write,
 };
 
-static int vsmmuv3_init_single(struct domain *d, paddr_t addr, paddr_t size)
+static int vsmmuv3_init_single(struct domain *d, paddr_t addr, paddr_t size,
+                               uint32_t features)
 {
     struct virt_smmu *smmu;
 
@@ -38,6 +43,7 @@ static int vsmmuv3_init_single(struct domain *d, paddr_t 
addr, paddr_t size)
         return -ENOMEM;
 
     smmu->d = d;
+    smmu->features = features;
 
     register_mmio_handler(d, &vsmmuv3_mmio_handler, addr, size, smmu);
 
@@ -50,23 +56,51 @@ static int vsmmuv3_init_single(struct domain *d, paddr_t 
addr, paddr_t size)
 static int domain_vsmmuv3_init(struct domain *d)
 {
     int ret;
+    struct host_iommu *hw_iommu;
+    const struct arm_smmu_device *smmu;
+
+    if ( list_head_is_null(&host_iommu_list) )
+        return -ENODEV;
 
     INIT_LIST_HEAD(&d->arch.viommu_list);
 
     if ( is_hardware_domain(d) )
     {
-        struct host_iommu *hw_iommu;
-
         list_for_each_entry(hw_iommu, &host_iommu_list, entry)
         {
-            ret = vsmmuv3_init_single(d, hw_iommu->addr, hw_iommu->size);
+            smmu = (const struct arm_smmu_device *) hw_iommu->iommu;
+            ret = vsmmuv3_init_single(d, hw_iommu->addr, hw_iommu->size,
+                                      smmu->features);
             if ( ret )
                 return ret;
         }
     }
     else
     {
-        ret = vsmmuv3_init_single(d, GUEST_VSMMUV3_BASE, GUEST_VSMMUV3_SIZE);
+        /*
+         * Require: For non-hardware domains, features must be uniform across
+         * physical SMMUs to avoid advertising features that are unsupported by
+         * certain units.
+         */
+        uint32_t features;
+        hw_iommu = list_first_entry(&host_iommu_list, struct host_iommu, 
entry);
+        smmu = (const struct arm_smmu_device *) hw_iommu->iommu;
+        features = smmu->features;
+        list_for_each_entry(hw_iommu, &host_iommu_list, entry)
+        {
+            smmu = (const struct arm_smmu_device *) hw_iommu->iommu;
+
+            if ( smmu->features != features )
+            {
+                printk(XENLOG_G_ERR
+                       "vSMMUv3: Unsupported HW layout: physical SMMUs must " \
+                       "have a uniform set of features\n");
+                return -EINVAL;
+            }
+        }
+
+        ret = vsmmuv3_init_single(d, GUEST_VSMMUV3_BASE, GUEST_VSMMUV3_SIZE,
+                                  features);
         if ( ret )
             return ret;
     }
-- 
2.43.0

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.