[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 1/6] xen: Add support for hiding and unhiding pcie passthrough devices
xen: Add support for hiding and unhiding pcie passthrough devices Add support for hiding and unhiding (by introducing two new hypercall subops) pci devices that trigger AER fatal errors while assigned to guests in passthrough mode. Hiding of the device is done by assigning it to dom_xen dummy domain. XEN_DOMCTL_hide_device subop is used after the domain is being destroyed and iommu pages and context are mapped to dom0. Unhiding is a reverse operation and done with XEN_DOMCTL_unhide_device called by the toolstack. XSM hooks used for device hide, unhide, test_hidden use respectively assign_device, deassign_device, and test_assign_device hooks. Signed-off-by: Venu Busireddy <venu.busireddy@xxxxxxxxxx> Signed-off-by: Elena Ufimtseva <elena.ufimtseva@xxxxxxxxxx> --- xen/common/domctl.c | 6 ++ xen/drivers/passthrough/pci.c | 161 ++++++++++++++++++++++++++++++++++-- xen/include/public/domctl.h | 3 + xen/include/xsm/dummy.h | 18 ++++ xen/include/xsm/xsm.h | 16 ++++ xen/xsm/dummy.c | 3 + xen/xsm/flask/hooks.c | 19 +++++ xen/xsm/flask/policy/access_vectors | 6 +- 8 files changed, 220 insertions(+), 12 deletions(-) diff --git a/xen/common/domctl.c b/xen/common/domctl.c index 951a5dc..5e0f123 100644 --- a/xen/common/domctl.c +++ b/xen/common/domctl.c @@ -393,9 +393,15 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) { case XEN_DOMCTL_createdomain: case XEN_DOMCTL_test_assign_device: + case XEN_DOMCTL_test_hidden_device: case XEN_DOMCTL_gdbsx_guestmemio: d = NULL; break; + case XEN_DOMCTL_hide_device: + case XEN_DOMCTL_unhide_device: + rcu_lock_domain(dom_xen); + d = dom_xen; + break; default: d = rcu_lock_domain_by_id(op->domain); if ( !d && op->cmd != XEN_DOMCTL_getdomaininfo ) diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c index c8e2d2d..ef4681a 100644 --- a/xen/drivers/passthrough/pci.c +++ b/xen/drivers/passthrough/pci.c @@ -31,6 +31,7 @@ #include <xen/softirq.h> #include <xen/tasklet.h> #include <xsm/xsm.h> +#include <xen/mm.h> #include <asm/msi.h> #include "ats.h" @@ -1333,19 +1334,31 @@ int iommu_remove_device(struct pci_dev *pdev) return hd->platform_ops->remove_device(pdev->devfn, pci_to_dev(pdev)); } +static bool device_assigned_to_domain(struct domain *d, u16 seg, u8 bus, u8 devfn) +{ + bool rc = false; + + pcidevs_lock(); + + if ( pci_get_pdev_by_domain(d, seg, bus, devfn) ) + rc = true; + + pcidevs_unlock(); + return rc; +} + /* * If the device isn't owned by the hardware domain, it means it already * has been assigned to other domain, or it doesn't exist. */ static int device_assigned(u16 seg, u8 bus, u8 devfn) { - struct pci_dev *pdev; - - pcidevs_lock(); - pdev = pci_get_pdev_by_domain(hardware_domain, seg, bus, devfn); - pcidevs_unlock(); + return device_assigned_to_domain(hardware_domain, seg, bus, devfn) ? 0 : -EBUSY; +} - return pdev ? 0 : -EBUSY; +static int device_hidden(u16 seg, u8 bus, u8 devfn) +{ + return device_assigned_to_domain(dom_xen, seg, bus, devfn) ? -EBUSY : 0; } static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) @@ -1354,6 +1367,22 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) struct pci_dev *pdev; int rc = 0; + if ( device_hidden(seg, bus, devfn) ) + return -EINVAL; + + if ( d == dom_xen ) + { + pdev = pci_get_pdev(seg, bus, devfn); + if ( pdev ) + { + list_move(&pdev->domain_list, &dom_xen->arch.pdev_list); + pdev->domain = dom_xen; + return rc; + } + else + return -ENODEV; + } + if ( !iommu_enabled || !hd->platform_ops ) return 0; @@ -1417,10 +1446,23 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) struct pci_dev *pdev = NULL; int ret = 0; + ASSERT(pcidevs_locked()); + + if ( d == dom_xen ) + { + pdev = pci_get_pdev(seg, bus, devfn); + if ( pdev ) + { + list_move(&pdev->domain_list, &hardware_domain->arch.pdev_list); + pdev->domain = hardware_domain; + return ret; + } + else return -ENODEV; + } + if ( !iommu_enabled || !hd->platform_ops ) return -EINVAL; - ASSERT(pcidevs_locked()); pdev = pci_get_pdev_by_domain(d, seg, bus, devfn); if ( !pdev ) return -ENODEV; @@ -1600,6 +1642,15 @@ int iommu_do_pci_domctl( seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); ret = -EINVAL; } + + if ( device_hidden(seg, bus, devfn) ) + { + printk(XENLOG_G_INFO + "%04x:%02x:%02x.%u device is hidden\n", + seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); + ret = -EINVAL; + } + break; case XEN_DOMCTL_assign_device: @@ -1636,8 +1687,15 @@ int iommu_do_pci_domctl( break; } - ret = device_assigned(seg, bus, devfn) ?: - assign_device(d, seg, bus, devfn, flag); + if ( device_hidden(seg, bus, devfn) ) + { + ret = -EINVAL; + break; + } + + if ( !device_assigned(seg, bus, devfn) ) + ret = assign_device(d, seg, bus, devfn, flag); + if ( ret == -ERESTART ) ret = hypercall_create_continuation(__HYPERVISOR_domctl, "h", u_domctl); @@ -1671,6 +1729,12 @@ int iommu_do_pci_domctl( bus = PCI_BUS(machine_sbdf); devfn = PCI_DEVFN2(machine_sbdf); + if ( device_hidden(seg, bus, devfn) ) + { + ret = -EINVAL; + break; + } + pcidevs_lock(); ret = deassign_device(d, seg, bus, devfn); pcidevs_unlock(); @@ -1679,7 +1743,86 @@ int iommu_do_pci_domctl( "deassign %04x:%02x:%02x.%u from dom%d failed (%d)\n", seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), d->domain_id, ret); + break; + + case XEN_DOMCTL_hide_device: + machine_sbdf = domctl->u.assign_device.u.pci.machine_sbdf; + ret = xsm_hide_device(XSM_HOOK, d, machine_sbdf); + if ( ret ) + break; + + if ( unlikely(d->is_dying) ) + { + ret = -EAGAIN; + break; + } + + seg = machine_sbdf >> 16; + bus = PCI_BUS(machine_sbdf); + devfn = PCI_DEVFN2(machine_sbdf); + flag = domctl->u.assign_device.flag; + + if ( device_hidden(seg, bus, devfn) ) + { + ret = -EINVAL; + break; + } + + pcidevs_lock(); + ret = assign_device(dom_xen, seg, bus, devfn, flag); + pcidevs_unlock(); + if ( ret == -ERESTART ) + ret = hypercall_create_continuation(__HYPERVISOR_domctl, + "h", u_domctl); + else if ( ret ) + printk(XENLOG_G_ERR "XEN_DOMCTL_hide_device: " + "hide %04x:%02x:%02x.%u failed (%d)\n", + seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), ret); + break; + + case XEN_DOMCTL_unhide_device: + machine_sbdf = domctl->u.assign_device.u.pci.machine_sbdf; + ret = xsm_unhide_device(XSM_HOOK, d, machine_sbdf); + if ( ret ) + break; + + if ( unlikely(d->is_dying) ) + { + ret = -EINVAL; + break; + } + + seg = machine_sbdf >> 16; + bus = PCI_BUS(machine_sbdf); + devfn = PCI_DEVFN2(machine_sbdf); + + if ( !device_hidden(seg, bus, devfn) ) + { + ret = -EINVAL; + break; + } + + pcidevs_lock(); + ret = deassign_device(dom_xen, seg, bus, devfn); + pcidevs_unlock(); + + if ( ret == -ERESTART ) + ret = hypercall_create_continuation(__HYPERVISOR_domctl, + "h", u_domctl); + else if ( ret ) + printk(XENLOG_G_ERR "XEN_DOMCTL_unhide_device: " + "assign %04x:%02x:%02x.%u to dom%d failed (%d)\n", + seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), + d->domain_id, ret); + break; + + case XEN_DOMCTL_test_hidden_device: + machine_sbdf = domctl->u.assign_device.u.pci.machine_sbdf; + seg = machine_sbdf >> 16; + bus = PCI_BUS(machine_sbdf); + devfn = PCI_DEVFN2(machine_sbdf); + ret = device_hidden(seg, bus, devfn); break; default: diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h index e6cf211..1b043ea 100644 --- a/xen/include/public/domctl.h +++ b/xen/include/public/domctl.h @@ -1222,6 +1222,9 @@ struct xen_domctl { #define XEN_DOMCTL_gdbsx_pausevcpu 1001 #define XEN_DOMCTL_gdbsx_unpausevcpu 1002 #define XEN_DOMCTL_gdbsx_domstatus 1003 +#define XEN_DOMCTL_hide_device 2001 +#define XEN_DOMCTL_unhide_device 2002 +#define XEN_DOMCTL_test_hidden_device 2003 uint32_t interface_version; /* XEN_DOMCTL_INTERFACE_VERSION */ domid_t domain; union { diff --git a/xen/include/xsm/dummy.h b/xen/include/xsm/dummy.h index 62fcea6..0b820e1 100644 --- a/xen/include/xsm/dummy.h +++ b/xen/include/xsm/dummy.h @@ -355,6 +355,24 @@ static XSM_INLINE int xsm_deassign_device(XSM_DEFAULT_ARG struct domain *d, uint return xsm_default_action(action, current->domain, d); } +static XSM_INLINE int xsm_hide_device(XSM_DEFAULT_ARG struct domain *d, uint32_t machine_bdf) +{ + XSM_ASSERT_ACTION(XSM_HOOK); + return xsm_default_action(action, current->domain, d); +} + +static XSM_INLINE int xsm_unhide_device(XSM_DEFAULT_ARG struct domain *d, uint32_t machine_bdf) +{ + XSM_ASSERT_ACTION(XSM_HOOK); + return xsm_default_action(action, current->domain, d); +} + +static XSM_INLINE int xsm_test_hidden_device(XSM_DEFAULT_ARG uint32_t machine_bdf) +{ + XSM_ASSERT_ACTION(XSM_HOOK); + return xsm_default_action(action, current->domain, NULL); +} + #endif /* HAS_PASSTHROUGH && HAS_PCI */ #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE) diff --git a/xen/include/xsm/xsm.h b/xen/include/xsm/xsm.h index 60c0fd6..03dbeff 100644 --- a/xen/include/xsm/xsm.h +++ b/xen/include/xsm/xsm.h @@ -479,6 +479,22 @@ static inline int xsm_deassign_device(xsm_default_t def, struct domain *d, uint3 { return xsm_ops->deassign_device(d, machine_bdf); } + +static inline int xsm_hide_device(xsm_default_t def, struct domain *d, uint32_t machine_bdf) +{ + return xsm_ops->hide_device(d, machine_bdf); +} + +static inline int xsm_unhide_device(xsm_default_t def, struct domain *d, uint32_t machine_bdf) +{ + return xsm_ops->unhide_device(d, machine_bdf); +} + +static inline int xsm_test_hidden_device(xsm_default_t def, uint32_t machine_bdf) +{ + return xsm_ops->test_hidden_device(machine_bdf); +} + #endif /* HAS_PASSTHROUGH && HAS_PCI) */ #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE) diff --git a/xen/xsm/dummy.c b/xen/xsm/dummy.c index 3cb5492..78111bb 100644 --- a/xen/xsm/dummy.c +++ b/xen/xsm/dummy.c @@ -94,6 +94,9 @@ void __init xsm_fixup_ops (struct xsm_operations *ops) set_to_dummy_if_null(ops, test_assign_device); set_to_dummy_if_null(ops, assign_device); set_to_dummy_if_null(ops, deassign_device); + set_to_dummy_if_null(ops, hide_device); + set_to_dummy_if_null(ops, unhide_device); + set_to_dummy_if_null(ops, test_hidden_device); #endif #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE) diff --git a/xen/xsm/flask/hooks.c b/xen/xsm/flask/hooks.c index fd84ac0..3695768 100644 --- a/xen/xsm/flask/hooks.c +++ b/xen/xsm/flask/hooks.c @@ -1311,6 +1311,22 @@ static int flask_deassign_device(struct domain *d, uint32_t machine_bdf) return avc_current_has_perm(rsid, SECCLASS_RESOURCE, RESOURCE__REMOVE_DEVICE, NULL); } + +static int flask_unhide_device(struct domain *d, uint32_t machine_bdf) +{ + return flask_deassign_device(d, machine_bdf); +} + +static int flask_hide_device(struct domain *d, uint32_t machine_bdf) +{ + return flask_assign_device(d, machine_bdf); +} + +static int flask_test_hidden_device(struct domain *d, uint32_t machine_bdf) +{ + return flask_test_assign_device(d, machine_bdf); +} + #endif /* HAS_PASSTHROUGH && HAS_PCI */ #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE) @@ -1783,6 +1799,9 @@ static struct xsm_operations flask_ops = { .test_assign_device = flask_test_assign_device, .assign_device = flask_assign_device, .deassign_device = flask_deassign_device, + .hide_device = flask_hide_device, + .unhide_device = flask_unhide_device, + .test_hidden_device = flask_test_hidden_device, #endif #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE) diff --git a/xen/xsm/flask/policy/access_vectors b/xen/xsm/flask/policy/access_vectors index 1f7eb35..873df59 100644 --- a/xen/xsm/flask/policy/access_vectors +++ b/xen/xsm/flask/policy/access_vectors @@ -437,13 +437,13 @@ class resource # XEN_DOMCTL_iomem_permission, XEN_DOMCTL_memory_mapping add_iomem remove_iomem -# XEN_DOMCTL_get_device_group, XEN_DOMCTL_test_assign_device: +# XEN_DOMCTL_get_device_group, XEN_DOMCTL_test_assign_device, XEN_DOMCTL_test_hidden_device: # source = domain making the hypercall # target = device being queried stat_device -# XEN_DOMCTL_assign_device +# XEN_DOMCTL_assign_device, XEN_DOMCTL_hide_device add_device -# XEN_DOMCTL_deassign_device +# XEN_DOMCTL_deassign_device, XEN_DOMCTL_unhide_device remove_device # checked for PCI hot and cold-plug hypercalls, with target as the PCI device # checked for CPU and memory hotplug with xen_t as the target _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx https://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |