@@ -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)
{
@@ -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__ */
@@ -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;
@@ -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)));
+ 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)));
+ 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)));
+ 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)));
+ if ( rc )
+ return rc;
+ }
+ else
+ {
+ rc = rangeset_remove_range(r, PFN_DOWN(__pa(&__2M_rwdata_start)),
+ PFN_DOWN(__pa(&__2M_rwdata_end)));
+ if ( rc )
+ return rc;
+ }
+
+ return 0;
+}
+
static int __hwdom_init cf_check io_bitmap_cb(
unsigned long s, unsigned long e, void *ctx)
{
@@ -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);