diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c index a0fee1ef13..b84a4483cc 100644 --- a/xen/arch/arm/domain_build.c +++ b/xen/arch/arm/domain_build.c @@ -1243,6 +1243,7 @@ static int __init map_device_children(struct domain *d, * - Give permission to the guest to manage IRQ and MMIO range * - Retrieve the IRQ configuration (i.e edge/level) from device tree * When the device is not marked for guest passthrough: + * - Try to call iommu_add_dt_device to protect the device by an IOMMU * - Assign the device to the guest if it's protected by an IOMMU * - Map the IRQs and iomem regions to DOM0 */ @@ -1263,16 +1264,30 @@ static int __init handle_device(struct domain *d, struct dt_device_node *dev, dt_dprintk("%s passthrough = %d nirq = %d naddr = %u\n", dt_node_full_name(dev), need_mapping, nirq, naddr); - if ( dt_device_is_protected(dev) && need_mapping ) + if ( need_mapping ) { - dt_dprintk("%s setup iommu\n", dt_node_full_name(dev)); - res = iommu_assign_dt_device(d, dev); - if ( res ) + dt_dprintk("Check if %s is behind the IOMMU and add it\n", + dt_node_full_name(dev)); + + res = iommu_add_dt_device(dev); + if ( res < 0 ) { - printk(XENLOG_ERR "Failed to setup the IOMMU for %s\n", + printk(XENLOG_ERR "Failed to add %s to the IOMMU\n", dt_node_full_name(dev)); return res; } + + if ( dt_device_is_protected(dev) ) + { + dt_dprintk("%s setup iommu\n", dt_node_full_name(dev)); + res = iommu_assign_dt_device(d, dev); + if ( res ) + { + printk(XENLOG_ERR "Failed to setup the IOMMU for %s\n", + dt_node_full_name(dev)); + return res; + } + } } /* Give permission and map IRQs */ diff --git a/xen/drivers/passthrough/device_tree.c b/xen/drivers/passthrough/device_tree.c index 921a6e5f1e..cc900bac70 100644 --- a/xen/drivers/passthrough/device_tree.c +++ b/xen/drivers/passthrough/device_tree.c @@ -22,6 +22,8 @@ #include #include +#include + static spinlock_t dtdevs_lock = SPIN_LOCK_UNLOCKED; int iommu_assign_dt_device(struct domain *d, struct dt_device_node *dev) @@ -125,6 +127,68 @@ int iommu_release_dt_devices(struct domain *d) return 0; } +int iommu_add_dt_device(struct dt_device_node *np) +{ + const struct iommu_ops *ops = iommu_get_ops(); + struct dt_phandle_args iommu_spec; + struct device *dev = dt_to_dev(np); + int rc = 1, index = 0; + + if ( !iommu_enabled ) + return 1; + + if ( !ops ) + return -EINVAL; + + if ( dev_iommu_fwspec_get(dev) ) + return -EEXIST; + + /* + * According to the Documentation/devicetree/bindings/iommu/iommu.txt + * from Linux. + */ + while ( !dt_parse_phandle_with_args(np, "iommus", "#iommu-cells", + index, &iommu_spec) ) + { + /* + * The driver which supports generic IOMMU DT bindings must have + * these callback implemented. + */ + if ( !ops->add_device || !ops->dt_xlate ) + return -EINVAL; + + if ( !dt_device_is_available(iommu_spec.np) ) + break; + + rc = iommu_fwspec_init(dev, &iommu_spec.np->dev); + if ( rc ) + break; + + /* + * Provide DT IOMMU specifier which describes the IOMMU master + * interfaces of that device (device IDs, etc) to the driver. + * The driver is responsible to decide how to interpret them. + */ + rc = ops->dt_xlate(dev, &iommu_spec); + if ( rc ) + break; + + index++; + } + + /* + * Add master device to the IOMMU if latter is present and available. + * The driver is responsible to mark that device as protected. + */ + if ( !rc ) + rc = ops->add_device(0, dev); + + if ( rc < 0 ) + iommu_fwspec_free(dev); + + return rc; +} + int iommu_do_dt_domctl(struct xen_domctl *domctl, struct domain *d, XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) { @@ -166,6 +230,19 @@ int iommu_do_dt_domctl(struct xen_domctl *domctl, struct domain *d, break; } + ret = iommu_add_dt_device(dev); + /* + * Ignore "-EEXIST" error code as it would mean that the device is + * already added to the IOMMU (positive result). Such happens after + * re-creating guest domain. + */ + if ( ret < 0 && ret != -EEXIST ) + { + printk(XENLOG_G_ERR "Failed to add %s to the IOMMU\n", + dt_node_full_name(dev)); + break; + } + ret = iommu_assign_dt_device(d, dev); if ( ret ) diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h index 7c3003f3f1..974bd3ffe8 100644 --- a/xen/include/xen/iommu.h +++ b/xen/include/xen/iommu.h @@ -182,6 +182,17 @@ int iommu_deassign_dt_device(struct domain *d, struct dt_device_node *dev); int iommu_dt_domain_init(struct domain *d); int iommu_release_dt_devices(struct domain *d); +/* + * Helper to add master device to the IOMMU using generic IOMMU DT bindings. + * + * Return values: + * 0 : device is protected by an IOMMU + * <0 : device is not protected by an IOMMU, but must be (error condition) + * >0 : device doesn't need to be protected by an IOMMU + * (IOMMU is not enabled/present or device is not connected to it). + */ +int iommu_add_dt_device(struct dt_device_node *np); + int iommu_do_dt_domctl(struct xen_domctl *, struct domain *, XEN_GUEST_HANDLE_PARAM(xen_domctl_t)); @@ -250,6 +261,16 @@ struct iommu_ops { int __must_check (*iotlb_flush_all)(struct domain *d); int (*get_reserved_device_memory)(iommu_grdm_t *, void *); void (*dump_p2m_table)(struct domain *d); + +#ifdef CONFIG_HAS_DEVICE_TREE + /* + * All IOMMU drivers which support generic IOMMU DT bindings should use + * this callback. This is a way for the framework to provide the driver + * with DT IOMMU specifier which describes the IOMMU master interfaces of + * that device (device IDs, etc). + */ + int (*dt_xlate)(device_t *dev, const struct dt_phandle_args *args); +#endif }; #include