|
[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
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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |