From: ddugger@redhat.com <ddugger@redhat.com> Date: Mon, 23 Mar 2009 10:23:10 -0600 Subject: [xen] x86: add domctl interfaces for VT-d Message-id: 200903231623.n2NGNAcI022071@sobek.n0ano.com O-Subject: [RHEL5.4 PATCH 8/21 V2] Add domctl interfaces for VT-d Bugzilla: 484227 RH-Acked-by: Chris Lalancette <clalance@redhat.com> RH-Acked-by: Gerd Hoffmann <kraxel@redhat.com> RH-Acked-by: Chris Lalancette <clalance@redhat.com> implement each XEN_DOMCTL_xxx of VT-d, Upstream Status: Accepted (CS 15916, 16592, 17747) BZ: 484227 Signed-off-by: Weidong Han <weidong.han@intel.com> Signed-off-by: Gerd Hoffman <kraxel@redhat.com> Signed-off-by: Don Dugger <donald.d.dugger@intel.com> diff --git a/arch/x86/domctl.c b/arch/x86/domctl.c index 9cd0c71..fd04c0b 100644 --- a/arch/x86/domctl.c +++ b/arch/x86/domctl.c @@ -24,6 +24,7 @@ #include <asm/hvm/hvm.h> #include <asm/hvm/support.h> #include <asm/processor.h> +#include <xen/iommu.h> long arch_do_domctl( struct xen_domctl *domctl, @@ -432,6 +433,276 @@ long arch_do_domctl( } break; + case XEN_DOMCTL_get_device_group: + { + struct domain *d; + u32 max_sdevs; + u8 bus, devfn; + XEN_GUEST_HANDLE_64(uint32_t) sdevs; + int num_sdevs; + + ret = -ENOSYS; + if ( !iommu_enabled ) + break; + + ret = -EINVAL; + if ( (d = rcu_lock_domain_by_id(domctl->domain)) == NULL ) + break; + + bus = (domctl->u.get_device_group.machine_bdf >> 16) & 0xff; + devfn = (domctl->u.get_device_group.machine_bdf >> 8) & 0xff; + max_sdevs = domctl->u.get_device_group.max_sdevs; + sdevs = domctl->u.get_device_group.sdev_array; + + num_sdevs = iommu_get_device_group(d, bus, devfn, sdevs, max_sdevs); + if ( num_sdevs < 0 ) + { + dprintk(XENLOG_ERR, "iommu_get_device_group() failed!\n"); + ret = -EFAULT; + domctl->u.get_device_group.num_sdevs = 0; + } + else + { + ret = 0; + domctl->u.get_device_group.num_sdevs = num_sdevs; + } + if ( copy_to_guest(u_domctl, domctl, 1) ) + ret = -EFAULT; + rcu_unlock_domain(d); + } + break; + + case XEN_DOMCTL_test_assign_device: + { + u8 bus, devfn; + + ret = -ENOSYS; + if ( !iommu_enabled ) + break; + + ret = -EINVAL; + bus = (domctl->u.assign_device.machine_bdf >> 16) & 0xff; + devfn = (domctl->u.assign_device.machine_bdf >> 8) & 0xff; + + if ( device_assigned(bus, devfn) ) + { + gdprintk(XENLOG_ERR, "XEN_DOMCTL_test_assign_device: " + "%x:%x:%x already assigned, or non-existent\n", + bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); + break; + } + ret = 0; + } + break; + + case XEN_DOMCTL_assign_device: + { + struct domain *d; + u8 bus, devfn; + + ret = -ENOSYS; + if ( !iommu_enabled ) + break; + + ret = -EINVAL; + if ( unlikely((d = get_domain_by_id(domctl->domain)) == NULL) ) + { + gdprintk(XENLOG_ERR, + "XEN_DOMCTL_assign_device: get_domain_by_id() failed\n"); + break; + } + + bus = (domctl->u.assign_device.machine_bdf >> 16) & 0xff; + devfn = (domctl->u.assign_device.machine_bdf >> 8) & 0xff; + + ret = assign_device(d, bus, devfn); + if ( ret ) + gdprintk(XENLOG_ERR, "XEN_DOMCTL_assign_device: " + "assign device (%x:%x:%x) failed\n", + bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); + + put_domain(d); + } + break; + + case XEN_DOMCTL_deassign_device: + { + struct domain *d; + u8 bus, devfn; + + ret = -ENOSYS; + if ( !iommu_enabled ) + break; + + ret = -EINVAL; + if ( unlikely((d = get_domain_by_id(domctl->domain)) == NULL) ) + { + gdprintk(XENLOG_ERR, + "XEN_DOMCTL_deassign_device: get_domain_by_id() failed\n"); + break; + } + + bus = (domctl->u.assign_device.machine_bdf >> 16) & 0xff; + devfn = (domctl->u.assign_device.machine_bdf >> 8) & 0xff; + + spin_lock(&pcidevs_lock); + ret = deassign_device(d, bus, devfn); + spin_unlock(&pcidevs_lock); + gdprintk(XENLOG_INFO, "XEN_DOMCTL_deassign_device: bdf = %x:%x:%x\n", + bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); + + put_domain(d); + } + break; + + case XEN_DOMCTL_bind_pt_irq: + { + struct domain * d; + xen_domctl_bind_pt_irq_t * bind; + + ret = -ESRCH; + if ( (d = rcu_lock_domain_by_id(domctl->domain)) == NULL ) + break; + bind = &(domctl->u.bind_pt_irq); + + ret = -ESRCH; + if ( iommu_enabled ) + ret = pt_irq_create_bind_vtd(d, bind); + if ( ret < 0 ) + gdprintk(XENLOG_ERR, "pt_irq_create_bind failed!\n"); + + rcu_unlock_domain(d); + } + break; + + case XEN_DOMCTL_unbind_pt_irq: + { + struct domain * d; + xen_domctl_bind_pt_irq_t * bind; + + ret = -ESRCH; + if ( (d = rcu_lock_domain_by_id(domctl->domain)) == NULL ) + break; + bind = &(domctl->u.bind_pt_irq); + if ( iommu_enabled ) + ret = pt_irq_destroy_bind_vtd(d, bind); + if ( ret < 0 ) + gdprintk(XENLOG_ERR, "pt_irq_destroy_bind failed!\n"); + rcu_unlock_domain(d); + } + break; + + case XEN_DOMCTL_memory_mapping: + { + struct domain *d; + unsigned long gfn = domctl->u.memory_mapping.first_gfn; + unsigned long mfn = domctl->u.memory_mapping.first_mfn; + unsigned long nr_mfns = domctl->u.memory_mapping.nr_mfns; + int i; + + ret = -EINVAL; + if ( (mfn + nr_mfns - 1) < mfn ) /* wrap? */ + break; + + ret = -ESRCH; + if ( unlikely((d = rcu_lock_domain_by_id(domctl->domain)) == NULL) ) + break; + + ret=0; + if ( domctl->u.memory_mapping.add_mapping ) + { + gdprintk(XENLOG_INFO, + "memory_map:add: gfn=%lx mfn=%lx nr_mfns=%lx\n", + gfn, mfn, nr_mfns); + + ret = iomem_permit_access(d, mfn, mfn + nr_mfns - 1); + for ( i = 0; i < nr_mfns; i++ ) + set_mmio_p2m_entry(d, gfn+i, _mfn(mfn+i)); + } + else + { + gdprintk(XENLOG_INFO, + "memory_map:remove: gfn=%lx mfn=%lx nr_mfns=%lx\n", + gfn, mfn, nr_mfns); + + for ( i = 0; i < nr_mfns; i++ ) + clear_mmio_p2m_entry(d, gfn+i); + ret = iomem_deny_access(d, mfn, mfn + nr_mfns - 1); + } + + rcu_unlock_domain(d); + } + break; + + case XEN_DOMCTL_ioport_mapping: + { +#define MAX_IOPORTS 0x10000 + struct domain *d; + struct hvm_iommu *hd; + unsigned int fgp = domctl->u.ioport_mapping.first_gport; + unsigned int fmp = domctl->u.ioport_mapping.first_mport; + unsigned int np = domctl->u.ioport_mapping.nr_ports; + struct g2m_ioport *g2m_ioport; + int found = 0; + + ret = -EINVAL; + if ( (np == 0) || (fgp > MAX_IOPORTS) || (fmp > MAX_IOPORTS) || + ((fgp + np) > MAX_IOPORTS) || ((fmp + np) > MAX_IOPORTS) ) + { + gdprintk(XENLOG_ERR, + "ioport_map:invalid:gport=%x mport=%x nr_ports=%x\n", + fgp, fmp, np); + break; + } + + ret = -ESRCH; + if ( unlikely((d = rcu_lock_domain_by_id(domctl->domain)) == NULL) ) + break; + + hd = domain_hvm_iommu(d); + if ( domctl->u.ioport_mapping.add_mapping ) + { + gdprintk(XENLOG_INFO, + "ioport_map:add f_gport=%x f_mport=%x np=%x\n", + fgp, fmp, np); + + list_for_each_entry(g2m_ioport, &hd->g2m_ioport_list, list) + if (g2m_ioport->mport == fmp ) + { + g2m_ioport->gport = fgp; + g2m_ioport->np = np; + found = 1; + break; + } + if ( !found ) + { + g2m_ioport = xmalloc(struct g2m_ioport); + g2m_ioport->gport = fgp; + g2m_ioport->mport = fmp; + g2m_ioport->np = np; + list_add_tail(&g2m_ioport->list, &hd->g2m_ioport_list); + } + ret = ioports_permit_access(d, fmp, fmp + np - 1); + } + else + { + gdprintk(XENLOG_INFO, + "ioport_map:remove f_gport=%x f_mport=%x np=%x\n", + fgp, fmp, np); + list_for_each_entry(g2m_ioport, &hd->g2m_ioport_list, list) + if ( g2m_ioport->mport == fmp ) + { + list_del(&g2m_ioport->list); + xfree(g2m_ioport); + break; + } + ret = ioports_deny_access(d, fmp, fmp + np - 1); + } + rcu_unlock_domain(d); + } + break; + + default: ret = -ENOSYS; break;