diff --git a/xen/arch/x86/hvm/io.c b/xen/arch/x86/hvm/io.c index d75af83ad0..a42854c52b 100644 --- a/xen/arch/x86/hvm/io.c +++ b/xen/arch/x86/hvm/io.c @@ -369,6 +369,22 @@ bool vpci_is_mmcfg_address(const struct domain *d, paddr_t addr) return vpci_mmcfg_find(d, addr); } +int __hwdom_init vpci_subtract_mmcfg(const struct domain *d, struct rangeset *r) +{ + const struct hvm_mmcfg *mmcfg; + + list_for_each_entry ( mmcfg, &d->arch.hvm.mmcfg_regions, next ) + { + int rc = rangeset_remove_range(r, PFN_DOWN(mmcfg->addr), + PFN_DOWN(mmcfg->addr + mmcfg->size - 1)); + + if ( rc ) + return rc; + } + + return 0; +} + static unsigned int vpci_mmcfg_decode_addr(const struct hvm_mmcfg *mmcfg, paddr_t addr, pci_sbdf_t *sbdf) { diff --git a/xen/arch/x86/include/asm/hvm/io.h b/xen/arch/x86/include/asm/hvm/io.h index a977316578..e1e5e6fe74 100644 --- a/xen/arch/x86/include/asm/hvm/io.h +++ b/xen/arch/x86/include/asm/hvm/io.h @@ -156,6 +156,9 @@ void destroy_vpci_mmcfg(struct domain *d); /* Check if an address is between a MMCFG region for a domain. */ bool vpci_is_mmcfg_address(const struct domain *d, paddr_t addr); +/* Remove MMCFG regions from a given rangeset. */ +int vpci_subtract_mmcfg(const struct domain *d, struct rangeset *r); + #endif /* __ASM_X86_HVM_IO_H__ */ diff --git a/xen/arch/x86/include/asm/setup.h b/xen/arch/x86/include/asm/setup.h index 9a460e4db8..cd07d98101 100644 --- a/xen/arch/x86/include/asm/setup.h +++ b/xen/arch/x86/include/asm/setup.h @@ -37,6 +37,7 @@ void discard_initial_images(void); void *bootstrap_map(const module_t *mod); int xen_in_range(unsigned long mfn); +int remove_xen_ranges(struct rangeset *r); extern uint8_t kbd_shift_flags; diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c index 897b7e9208..ee233c69f1 100644 --- a/xen/arch/x86/setup.c +++ b/xen/arch/x86/setup.c @@ -2138,6 +2138,54 @@ int __hwdom_init xen_in_range(unsigned long mfn) return 0; } +int __hwdom_init remove_xen_ranges(struct rangeset *r) +{ + paddr_t start, end; + int rc; + + /* S3 resume code (and other real mode trampoline code) */ + rc = rangeset_remove_range(r, PFN_DOWN(bootsym_phys(trampoline_start)), + PFN_DOWN(bootsym_phys(trampoline_end) - 1)); + if ( rc ) + return rc; + + /* + * This needs to remain in sync with the uses of the same symbols in + * - __start_xen() + * - is_xen_fixed_mfn() + * - tboot_shutdown() + */ + /* hypervisor .text + .rodata */ + rc = rangeset_remove_range(r, PFN_DOWN(__pa(&_stext)), + PFN_DOWN(__pa(&__2M_rodata_end) - 1)); + if ( rc ) + return rc; + + /* hypervisor .data + .bss */ + if ( efi_boot_mem_unused(&start, &end) ) + { + ASSERT(__pa(start) >= __pa(&__2M_rwdata_start)); + rc = rangeset_remove_range(r, PFN_DOWN(__pa(&__2M_rwdata_start)), + PFN_DOWN(__pa(start) - 1)); + if ( rc ) + return rc; + ASSERT(__pa(end) <= __pa(&__2M_rwdata_end)); + rc = rangeset_remove_range(r, PFN_DOWN(__pa(end)), + PFN_DOWN(__pa(&__2M_rwdata_end) - 1)); + if ( rc ) + return rc; + } + else + { + rc = rangeset_remove_range(r, PFN_DOWN(__pa(&__2M_rwdata_start)), + PFN_DOWN(__pa(&__2M_rwdata_end) - 1)); + if ( rc ) + return rc; + } + + return 0; +} + static int __hwdom_init cf_check io_bitmap_cb( unsigned long s, unsigned long e, void *ctx) { diff --git a/xen/drivers/passthrough/x86/iommu.c b/xen/drivers/passthrough/x86/iommu.c index 59b0c7e980..fc5215a9dc 100644 --- a/xen/drivers/passthrough/x86/iommu.c +++ b/xen/drivers/passthrough/x86/iommu.c @@ -370,6 +370,14 @@ static unsigned int __hwdom_init hwdom_iommu_map(const struct domain *d, return perms; } +static int __hwdom_init cf_check map_subtract(unsigned long s, unsigned long e, + void *data) +{ + struct rangeset *map = data; + + return rangeset_remove_range(map, s, e); +} + struct map_data { struct domain *d; unsigned int flush_flags; @@ -533,6 +541,59 @@ void __hwdom_init arch_iommu_hwdom_init(struct domain *d) goto commit; } + /* Remove any areas in-use by Xen. */ + rc = remove_xen_ranges(map); + if ( rc ) + panic("IOMMU failed to remove Xen ranges: %d\n", rc); + + /* Remove any overlap with the Interrupt Address Range. */ + rc = rangeset_remove_range(map, 0xfee00, 0xfeeff); + if ( rc ) + panic("IOMMU failed to remove Interrupt Address Range: %d\n", rc); + + /* If emulating IO-APIC(s) make sure the base address is unmapped. */ + if ( has_vioapic(d) ) + { + for ( i = 0; i < d->arch.hvm.nr_vioapics; i++ ) + { + rc = rangeset_remove_singleton(map, + PFN_DOWN(domain_vioapic(d, i)->base_address)); + if ( rc ) + panic("IOMMU failed to remove IO-APIC: %d\n", rc); + } + } + + if ( is_pv_domain(d) ) + { + /* + * Be consistent with CPU mappings: Dom0 is permitted to establish r/o + * ones there (also for e.g. HPET in certain cases), so it should also + * have such established for IOMMUs. Remove any read-only ranges here, + * since ranges in mmio_ro_ranges are already explicitly mapped below + * in read-only mode. + */ + rc = rangeset_report_ranges(mmio_ro_ranges, 0, ~0UL, map_subtract, map); + if ( rc ) + panic("IOMMU failed to remove read-only regions: %d\n", rc); + } + + if ( has_vpci(d) ) + { + /* + * TODO: runtime added MMCFG regions are not checked to make sure they + * don't overlap with already mapped regions, thus preventing trapping. + */ + rc = vpci_subtract_mmcfg(d, map); + if ( rc ) + panic("IOMMU unable to remove MMCFG areas: %d\n", rc); + } + + /* Remove any regions past the last address addressable by the domain. */ + rc = rangeset_remove_range(map, PFN_DOWN(1UL << domain_max_paddr_bits(d)), + ~0UL); + if ( rc ) + panic("IOMMU unable to remove unaddressable ranges: %d\n", rc); + if ( iommu_verbose ) printk(XENLOG_INFO "%pd: identity mappings for IOMMU:\n", d);