diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c index 692565757e..5ff6ce15f6 100644 --- a/xen/arch/arm/p2m.c +++ b/xen/arch/arm/p2m.c @@ -34,7 +34,11 @@ static unsigned int __read_mostly max_vmid = MAX_VMID_8_BIT; #define P2M_ROOT_PAGES (1<mm64.pa_range < pa_range ) - pa_range = info->mm64.pa_range; + + /* + * Restrict "p2m_ipa_bits" if needed. As P2M table is always configured + * with IPA bits == PA bits, compare against "pabits". + */ + if ( pa_range_info[info->mm64.pa_range].pabits < p2m_ipa_bits ) + p2m_ipa_bits = pa_range_info[info->mm64.pa_range].pabits; /* Set a flag if the current cpu does not support 16 bit VMIDs. */ if ( info->mm64.vmid_bits != MM64_VMID_16_BITS_SUPPORT ) @@ -2003,6 +2026,16 @@ void __init setup_virt_paging(void) if ( !vmid_8_bit ) max_vmid = MAX_VMID_16_BIT; + /* Choose suitable "pa_range" according to the resulted "p2m_ipa_bits". */ + for ( i = 0; i < ARRAY_SIZE(pa_range_info); i++ ) + { + if ( p2m_ipa_bits == pa_range_info[i].pabits ) + { + pa_range = i; + break; + } + } + /* pa_range is 4 bits, but the defined encodings are only 3 bits */ if ( pa_range >= ARRAY_SIZE(pa_range_info) || !pa_range_info[pa_range].pabits ) panic("Unknown encoding of ID_AA64MMFR0_EL1.PARange %x\n", pa_range); diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c index fca7e68cba..790eab94d6 100644 --- a/xen/arch/arm/setup.c +++ b/xen/arch/arm/setup.c @@ -928,12 +928,17 @@ void __init start_xen(unsigned long boot_phys_offset, printk("Brought up %ld CPUs\n", (long)num_online_cpus()); /* TODO: smp_cpus_done(); */ - setup_virt_paging(); - + /* + * The IOMMU subsystem must be initialized before P2M as we need + * to gather requirements regarding the maximum IPA bits supported by + * each IOMMU device. + */ rc = iommu_setup(); if ( !iommu_enabled && rc != -ENODEV ) panic("Couldn't configure correctly all the IOMMUs.\n"); + setup_virt_paging(); + do_initcalls(); /* diff --git a/xen/drivers/passthrough/arm/ipmmu-vmsa.c b/xen/drivers/passthrough/arm/ipmmu-vmsa.c index f2fb4a2378..9cfae7e74e 100644 --- a/xen/drivers/passthrough/arm/ipmmu-vmsa.c +++ b/xen/drivers/passthrough/arm/ipmmu-vmsa.c @@ -844,22 +844,8 @@ static int ipmmu_probe(struct dt_device_node *node) goto out; } - /* - * As 4-level translation table is not supported in IPMMU, we need - * to check IPA size used for P2M table beforehand to be sure it is - * 3-level and the IPMMU will be able to use it. - * - * TODO: First initialize the IOMMU and gather the requirements and - * then initialize the P2M. In the P2M code, take into the account - * the IOMMU requirements and restrict "pa_range" if necessary. - */ - if ( IPMMU_MAX_P2M_IPA_BITS < p2m_ipa_bits ) - { - printk(XENLOG_ERR "ipmmu: P2M IPA size is not supported (P2M=%u IPMMU=%u)!\n", - p2m_ipa_bits, IPMMU_MAX_P2M_IPA_BITS); - ret = -ENODEV; - goto out; - } + /* Set maximum Stage-2 input size supported by the IPMMU. */ + p2m_restrict_ipa_bits(IPMMU_MAX_P2M_IPA_BITS); irq = platform_get_irq(node, 0); if ( irq < 0 ) diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c index 8ae986a18d..94662a8501 100644 --- a/xen/drivers/passthrough/arm/smmu.c +++ b/xen/drivers/passthrough/arm/smmu.c @@ -1110,7 +1110,11 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain) reg = TTBCR_TG0_64K; if (!stage1) { - reg |= (64 - smmu->s2_input_size) << TTBCR_T0SZ_SHIFT; + /* + * Xen: The IOMMU share the page-tables with the P2M + * which may have restrict the size further. + */ + reg |= (64 - p2m_ipa_bits) << TTBCR_T0SZ_SHIFT; switch (smmu->s2_output_size) { case 32: @@ -2198,14 +2202,9 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) size = arm_smmu_id_size_to_bits((id >> ID2_IAS_SHIFT) & ID2_IAS_MASK); smmu->s1_output_size = min_t(unsigned long, PHYS_MASK_SHIFT, size); - /* Xen: Stage-2 input size has to match p2m_ipa_bits. */ - if (size < p2m_ipa_bits) { - dev_err(smmu->dev, - "P2M IPA size not supported (P2M=%u SMMU=%lu)!\n", - p2m_ipa_bits, size); - return -ENODEV; - } - smmu->s2_input_size = p2m_ipa_bits; + /* Xen: Set maximum Stage-2 input size supported by the SMMU. */ + p2m_restrict_ipa_bits(size); + smmu->s2_input_size = size; #if 0 /* Stage-2 input size limited due to pgd allocation (PTRS_PER_PGD) */ #ifdef CONFIG_64BIT diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h index 03f2ee75c1..89f82df380 100644 --- a/xen/include/asm-arm/p2m.h +++ b/xen/include/asm-arm/p2m.h @@ -159,6 +159,15 @@ void p2m_altp2m_check(struct vcpu *v, uint16_t idx) /* Not supported on ARM. */ } +/* + * Helper to restrict "p2m_ipa_bits" according the external entity + * (e.g. IOMMU) requirements. + * + * Each corresponding driver should report the maximum IPA bits + * (Stage-2 input size) it can support. + */ +void p2m_restrict_ipa_bits(unsigned int ipa_bits); + /* Second stage paging setup, to be called on all CPUs */ void setup_virt_paging(void);