@@ -51,12 +51,6 @@ static int __init parse_dom0_mem(const char *s)
}
custom_param("dom0_mem", parse_dom0_mem);
-struct map_range_data
-{
- struct domain *d;
- p2m_type_t p2mt;
-};
-
/* Override macros from asm/page.h to make them work with mfn_t */
#undef virt_to_mfn
#define virt_to_mfn(va) _mfn(__virt_to_mfn(va))
@@ -1720,10 +1714,10 @@ static int __init map_dt_irq_to_domain(const struct dt_device_node *dev,
const struct dt_irq *dt_irq,
void *data)
{
- struct domain *d = data;
+ struct map_range_data *mr_data = data;
+ struct domain *d = mr_data->d;
unsigned int irq = dt_irq->irq;
int res;
- bool need_mapping = !dt_device_for_passthrough(dev);
if ( irq < NR_LOCAL_IRQS )
{
@@ -1742,18 +1736,16 @@ static int __init map_dt_irq_to_domain(const struct dt_device_node *dev,
return res;
}
- res = map_irq_to_domain(d, irq, need_mapping, dt_node_name(dev));
+ res = map_irq_to_domain(d, irq, !mr_data->skip_mapping, dt_node_name(dev));
return 0;
}
-static int __init map_range_to_domain(const struct dt_device_node *dev,
- u64 addr, u64 len,
- void *data)
+int __init map_range_to_domain(const struct dt_device_node *dev,
+ u64 addr, u64 len, void *data)
{
struct map_range_data *mr_data = data;
struct domain *d = mr_data->d;
- bool need_mapping = !dt_device_for_passthrough(dev);
int res;
res = iomem_permit_access(d, paddr_to_pfn(addr),
@@ -1767,7 +1759,7 @@ static int __init map_range_to_domain(const struct dt_device_node *dev,
return res;
}
- if ( need_mapping )
+ if ( !mr_data->skip_mapping )
{
res = map_regions_p2mt(d,
gaddr_to_gfn(addr),
@@ -1796,23 +1788,21 @@ static int __init map_range_to_domain(const struct dt_device_node *dev,
* then we may need to perform additional mappings in order to make
* the child resources available to domain 0.
*/
-static int __init map_device_children(struct domain *d,
- const struct dt_device_node *dev,
- p2m_type_t p2mt)
+static int __init map_device_children(const struct dt_device_node *dev,
+ struct map_range_data *mr_data)
{
- struct map_range_data mr_data = { .d = d, .p2mt = p2mt };
- int ret;
-
if ( dt_device_type_is_equal(dev, "pci") )
{
+ int ret;
+
dt_dprintk("Mapping children of %s to guest\n",
dt_node_full_name(dev));
- ret = dt_for_each_irq_map(dev, &map_dt_irq_to_domain, d);
+ ret = dt_for_each_irq_map(dev, &map_dt_irq_to_domain, mr_data);
if ( ret < 0 )
return ret;
- ret = dt_for_each_range(dev, &map_range_to_domain, &mr_data);
+ ret = dt_for_each_range(dev, &map_range_to_domain, mr_data);
if ( ret < 0 )
return ret;
}
@@ -1892,14 +1882,28 @@ static int __init handle_device(struct domain *d, struct dt_device_node *dev,
unsigned int i;
int res;
u64 addr, size;
- bool need_mapping = !dt_device_for_passthrough(dev);
+ bool own_device = !dt_device_for_passthrough(dev);
+ /*
+ * We want to avoid mapping the MMIO in dom0 for the following cases:
+ * - The device is owned by dom0 (i.e. it has been flagged for
+ * passthrough).
+ * - PCI host bridges with driver in Xen. They will later be mapped by
+ * pci_host_bridge_mappings().
+ */
+ struct map_range_data mr_data = {
+ .d = d,
+ .p2mt = p2mt,
+ .skip_mapping = !own_device ||
+ (is_pci_passthrough_enabled() &&
+ (device_get_class(dev) == DEVICE_PCI_HOSTBRIDGE))
+ };
naddr = dt_number_of_address(dev);
dt_dprintk("%s passthrough = %d naddr = %u\n",
- dt_node_full_name(dev), need_mapping, naddr);
+ dt_node_full_name(dev), own_device, naddr);
- if ( need_mapping )
+ if ( own_device )
{
dt_dprintk("Check if %s is behind the IOMMU and add it\n",
dt_node_full_name(dev));
@@ -1925,14 +1929,13 @@ static int __init handle_device(struct domain *d, struct dt_device_node *dev,
}
}
- res = handle_device_interrupts(d, dev, need_mapping);
+ res = handle_device_interrupts(d, dev, own_device);
if ( res < 0 )
return res;
/* Give permission and map MMIOs */
for ( i = 0; i < naddr; i++ )
{
- struct map_range_data mr_data = { .d = d, .p2mt = p2mt };
res = dt_device_get_address(dev, i, &addr, &size);
if ( res )
{
@@ -1946,7 +1949,7 @@ static int __init handle_device(struct domain *d, struct dt_device_node *dev,
return res;
}
- res = map_device_children(d, dev, p2mt);
+ res = map_device_children(dev, &mr_data);
if ( res )
return res;
@@ -3105,7 +3108,14 @@ static int __init construct_dom0(struct domain *d)
return rc;
if ( acpi_disabled )
+ {
rc = prepare_dtb_hwdom(d, &kinfo);
+ if ( rc < 0 )
+ return rc;
+#ifdef CONFIG_HAS_PCI
+ rc = pci_host_bridge_mappings(d);
+#endif
+ }
else
rc = prepare_acpi(d, &kinfo);
@@ -40,6 +40,19 @@ void __iomem *pci_ecam_map_bus(struct pci_host_bridge *bridge,
return base + (PCI_DEVFN2(sbdf.bdf) << devfn_shift) + where;
}
+bool __init pci_ecam_need_p2m_hwdom_mapping(struct domain *d,
+ struct pci_host_bridge *bridge,
+ uint64_t addr)
+{
+ struct pci_config_window *cfg = bridge->cfg;
+
+ /*
+ * We do not want ECAM address space to be mapped in Domain-0's p2m,
+ * so we can trap access to it.
+ */
+ return cfg->phys_addr != addr;
+}
+
/* ECAM ops */
const struct pci_ecam_ops pci_generic_ecam_ops = {
.bus_shift = 20,
@@ -47,6 +60,7 @@ const struct pci_ecam_ops pci_generic_ecam_ops = {
.map_bus = pci_ecam_map_bus,
.read = pci_generic_config_read,
.write = pci_generic_config_write,
+ .need_p2m_hwdom_mapping = pci_ecam_need_p2m_hwdom_mapping,
}
};
@@ -22,6 +22,8 @@
#include <xen/sched.h>
#include <xen/vmap.h>
+#include <asm/setup.h>
+
/*
* List for all the pci host bridges.
*/
@@ -313,6 +315,54 @@ int pci_host_iterate_bridges_and_count(struct domain *d,
return count;
}
+/*
+ * For each PCI host bridge we need to only map those ranges
+ * which are used by Domain-0 to properly initialize the bridge,
+ * e.g. we do not want to map ECAM configuration space which lives in
+ * "reg" device tree property, but we want to map other regions of
+ * the host bridge. The PCI aperture defined by the "ranges" device
+ * tree property should also be skipped.
+ */
+int __init pci_host_bridge_mappings(struct domain *d)
+{
+ struct pci_host_bridge *bridge;
+ struct map_range_data mr_data = {
+ .d = d,
+ .p2mt = p2m_mmio_direct_dev,
+ .skip_mapping = false
+ };
+
+ list_for_each_entry( bridge, &pci_host_bridges, node )
+ {
+ const struct dt_device_node *dev = bridge->dt_node;
+ unsigned int i;
+
+ for ( i = 0; i < dt_number_of_address(dev); i++ )
+ {
+ uint64_t addr, size;
+ int err;
+
+ err = dt_device_get_address(dev, i, &addr, &size);
+ if ( err )
+ {
+ printk(XENLOG_ERR
+ "Unable to retrieve address range index=%u for %s\n",
+ i, dt_node_full_name(dev));
+ return err;
+ }
+
+ if ( bridge->ops->need_p2m_hwdom_mapping(d, bridge, addr) )
+ {
+ err = map_range_to_domain(dev, addr, size, &mr_data);
+ if ( err )
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
+
/*
* Local variables:
* mode: C
@@ -34,6 +34,7 @@ const struct pci_ecam_ops nwl_pcie_ops = {
.map_bus = pci_ecam_map_bus,
.read = pci_generic_config_read,
.write = pci_generic_config_write,
+ .need_p2m_hwdom_mapping = pci_ecam_need_p2m_hwdom_mapping,
}
};
@@ -17,6 +17,8 @@
#ifdef CONFIG_HAS_PCI
+#include <asm/p2m.h>
+
#define pci_to_dev(pcidev) (&(pcidev)->arch.dev)
extern bool pci_passthrough_enabled;
@@ -73,6 +75,9 @@ struct pci_ops {
uint32_t reg, uint32_t len, uint32_t *value);
int (*write)(struct pci_host_bridge *bridge, pci_sbdf_t sbdf,
uint32_t reg, uint32_t len, uint32_t value);
+ bool (*need_p2m_hwdom_mapping)(struct domain *d,
+ struct pci_host_bridge *bridge,
+ uint64_t addr);
};
/*
@@ -96,6 +101,9 @@ int pci_generic_config_write(struct pci_host_bridge *bridge, pci_sbdf_t sbdf,
uint32_t reg, uint32_t len, uint32_t value);
void __iomem *pci_ecam_map_bus(struct pci_host_bridge *bridge,
pci_sbdf_t sbdf, uint32_t where);
+bool pci_ecam_need_p2m_hwdom_mapping(struct domain *d,
+ struct pci_host_bridge *bridge,
+ uint64_t addr);
struct pci_host_bridge *pci_find_host_bridge(uint16_t segment, uint8_t bus);
struct dt_device_node *pci_find_host_bridge_node(struct device *dev);
int pci_get_host_bridge_segment(const struct dt_device_node *node,
@@ -114,6 +122,8 @@ int pci_host_iterate_bridges_and_count(struct domain *d,
int (*cb)(struct domain *d,
struct pci_host_bridge *bridge));
+int pci_host_bridge_mappings(struct domain *d);
+
#else /*!CONFIG_HAS_PCI*/
struct arch_pci_dev { };
@@ -2,6 +2,8 @@
#define __ARM_SETUP_H_
#include <public/version.h>
+#include <asm/p2m.h>
+#include <xen/device_tree.h>
#define MIN_FDT_ALIGN 8
#define MAX_FDT_SIZE SZ_2M
@@ -77,6 +79,14 @@ struct bootinfo {
#endif
};
+struct map_range_data
+{
+ struct domain *d;
+ p2m_type_t p2mt;
+ /* Set if mapping of the memory ranges must be skipped. */
+ bool skip_mapping;
+};
+
extern struct bootinfo bootinfo;
extern domid_t max_init_domid;
@@ -124,6 +134,9 @@ void device_tree_get_reg(const __be32 **cell, u32 address_cells,
u32 device_tree_get_u32(const void *fdt, int node,
const char *prop_name, u32 dflt);
+int map_range_to_domain(const struct dt_device_node *dev,
+ u64 addr, u64 len, void *data);
+
#endif
/*
* Local variables: