iommu.c

来自「xen虚拟机源代码安装包」· C语言 代码 · 共 348 行

C
348
字号
/* * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307 USA. */#include <xen/sched.h>#include <xen/iommu.h>#include <xen/paging.h>#include <xen/guest_access.h>extern struct iommu_ops intel_iommu_ops;extern struct iommu_ops amd_iommu_ops;static void parse_iommu_param(char *s);static int iommu_populate_page_table(struct domain *d);int intel_vtd_setup(void);int amd_iov_detect(void);/* * The 'iommu' parameter enables the IOMMU.  Optional comma separated * value may contain: * *   off|no|false|disable       Disable IOMMU (default) *   pv                         Enable IOMMU for PV domains *   no-pv                      Disable IOMMU for PV domains (default) *   force|required             Don't boot unless IOMMU is enabled */custom_param("iommu", parse_iommu_param);int iommu_enabled = 0;int iommu_pv_enabled = 0;int force_iommu = 0;static void __init parse_iommu_param(char *s){    char *ss;    iommu_enabled = 1;    do {        ss = strchr(s, ',');        if ( ss )            *ss = '\0';        if ( !strcmp(s, "off") || !strcmp(s, "no") || !strcmp(s, "false") ||             !strcmp(s, "0") || !strcmp(s, "disable") )            iommu_enabled = 0;        else if ( !strcmp(s, "pv") )            iommu_pv_enabled = 1;        else if ( !strcmp(s, "no-pv") )            iommu_pv_enabled = 0;        else if ( !strcmp(s, "force") || !strcmp(s, "required") )            force_iommu = 1;        s = ss + 1;    } while ( ss );}int iommu_domain_init(struct domain *domain){    struct hvm_iommu *hd = domain_hvm_iommu(domain);    spin_lock_init(&hd->mapping_lock);    INIT_LIST_HEAD(&hd->g2m_ioport_list);    if ( !iommu_enabled )        return 0;    switch ( boot_cpu_data.x86_vendor )    {    case X86_VENDOR_INTEL:        hd->platform_ops = &intel_iommu_ops;        break;    case X86_VENDOR_AMD:        hd->platform_ops = &amd_iommu_ops;        break;    default:        BUG();    }    return hd->platform_ops->init(domain);}int iommu_add_device(struct pci_dev *pdev){    struct hvm_iommu *hd;    if ( !pdev->domain )        return -EINVAL;    hd = domain_hvm_iommu(pdev->domain);    if ( !iommu_enabled || !hd->platform_ops )        return 0;    return hd->platform_ops->add_device(pdev);}int iommu_remove_device(struct pci_dev *pdev){    struct hvm_iommu *hd;    if ( !pdev->domain )        return -EINVAL;    hd = domain_hvm_iommu(pdev->domain);    if ( !iommu_enabled || !hd->platform_ops )        return 0;    return hd->platform_ops->remove_device(pdev);}int assign_device(struct domain *d, u8 bus, u8 devfn){    struct hvm_iommu *hd = domain_hvm_iommu(d);    int rc;    if ( !iommu_enabled || !hd->platform_ops )        return 0;    if ( (rc = hd->platform_ops->assign_device(d, bus, devfn)) )        return rc;    if ( has_arch_pdevs(d) && !is_hvm_domain(d) && !need_iommu(d) )    {        d->need_iommu = 1;        return iommu_populate_page_table(d);    }    return 0;}static int iommu_populate_page_table(struct domain *d){    struct hvm_iommu *hd = domain_hvm_iommu(d);    struct page_info *page;    int rc;    spin_lock(&d->page_alloc_lock);    list_for_each_entry ( page, &d->page_list, list )    {        if ( (page->u.inuse.type_info & PGT_type_mask) == PGT_writable_page )        {            rc = hd->platform_ops->map_page(                d, mfn_to_gmfn(d, page_to_mfn(page)), page_to_mfn(page));            if (rc)            {                spin_unlock(&d->page_alloc_lock);                hd->platform_ops->teardown(d);                return rc;            }        }    }    spin_unlock(&d->page_alloc_lock);    return 0;}void iommu_domain_destroy(struct domain *d){    struct hvm_iommu *hd  = domain_hvm_iommu(d);    struct list_head *ioport_list, *tmp;    struct g2m_ioport *ioport;    if ( !iommu_enabled || !hd->platform_ops )        return;    if ( !is_hvm_domain(d) && !need_iommu(d)  )        return;    if ( need_iommu(d) )    {        d->need_iommu = 0;        hd->platform_ops->teardown(d);        return;    }    if ( hd )    {        list_for_each_safe ( ioport_list, tmp, &hd->g2m_ioport_list )        {            ioport = list_entry(ioport_list, struct g2m_ioport, list);            list_del(&ioport->list);            xfree(ioport);        }    }    return hd->platform_ops->teardown(d);}int iommu_map_page(struct domain *d, unsigned long gfn, unsigned long mfn){    struct hvm_iommu *hd = domain_hvm_iommu(d);    if ( !iommu_enabled || !hd->platform_ops )        return 0;    return hd->platform_ops->map_page(d, gfn, mfn);}int iommu_unmap_page(struct domain *d, unsigned long gfn){    struct hvm_iommu *hd = domain_hvm_iommu(d);    if ( !iommu_enabled || !hd->platform_ops )        return 0;    return hd->platform_ops->unmap_page(d, gfn);}void deassign_device(struct domain *d, u8 bus, u8 devfn){    struct hvm_iommu *hd = domain_hvm_iommu(d);    if ( !iommu_enabled || !hd->platform_ops )        return;    hd->platform_ops->reassign_device(d, dom0, bus, devfn);    if ( !has_arch_pdevs(d) && need_iommu(d) )    {        d->need_iommu = 0;        hd->platform_ops->teardown(d);    }}static int iommu_setup(void){    int rc = -ENODEV;    if ( !iommu_enabled )        goto out;    switch ( boot_cpu_data.x86_vendor )    {    case X86_VENDOR_INTEL:        rc = intel_vtd_setup();        break;    case X86_VENDOR_AMD:        rc = amd_iov_detect();        break;    }    iommu_enabled = (rc == 0); out:    if ( force_iommu && !iommu_enabled )        panic("IOMMU setup failed, crash Xen for security purpose!\n");    if ( !iommu_enabled )        iommu_pv_enabled = 0;    printk("I/O virtualisation %sabled\n", iommu_enabled ? "en" : "dis");    if ( iommu_enabled )        printk("I/O virtualisation for PV guests %sabled\n",               iommu_pv_enabled ? "en" : "dis");    return rc;}__initcall(iommu_setup);int iommu_get_device_group(struct domain *d, u8 bus, u8 devfn,     XEN_GUEST_HANDLE_64(uint32) buf, int max_sdevs){    struct hvm_iommu *hd = domain_hvm_iommu(d);    struct pci_dev *pdev;    int group_id, sdev_id;    u32 bdf;    int i = 0;    struct iommu_ops *ops = hd->platform_ops;    if ( !iommu_enabled || !ops || !ops->get_device_group_id )        return 0;    group_id = ops->get_device_group_id(bus, devfn);    read_lock(&pcidevs_lock);    for_each_pdev( d, pdev )    {        if ( (pdev->bus == bus) && (pdev->devfn == devfn) )            continue;        sdev_id = ops->get_device_group_id(pdev->bus, pdev->devfn);        if ( (sdev_id == group_id) && (i < max_sdevs) )        {            bdf = 0;            bdf |= (pdev->bus & 0xff) << 16;            bdf |= (pdev->devfn & 0xff) << 8;            if ( unlikely(copy_to_guest_offset(buf, i, &bdf, 1)) )            {                read_unlock(&pcidevs_lock);                return -1;            }            i++;        }    }    read_unlock(&pcidevs_lock);    return i;}void iommu_update_ire_from_apic(    unsigned int apic, unsigned int reg, unsigned int value){    struct iommu_ops *ops = NULL;    switch ( boot_cpu_data.x86_vendor )    {    case X86_VENDOR_INTEL:        ops = &intel_iommu_ops;        break;    case X86_VENDOR_AMD:        ops = &amd_iommu_ops;        break;    default:        BUG();    }    ops->update_ire_from_apic(apic, reg, value);}void iommu_update_ire_from_msi(    struct msi_desc *msi_desc, struct msi_msg *msg){    struct iommu_ops *ops = NULL;    switch ( boot_cpu_data.x86_vendor )    {    case X86_VENDOR_INTEL:        ops = &intel_iommu_ops;        break;    case X86_VENDOR_AMD:        ops = &amd_iommu_ops;        break;    default:        BUG();    }    ops->update_ire_from_msi(msi_desc, msg);}/* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?