⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 iommu.c

📁 xen虚拟机源代码安装包
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * Copyright (c) 2006, Intel Corporation. * * 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. * * Copyright (C) Ashok Raj <ashok.raj@intel.com> * Copyright (C) Shaohua Li <shaohua.li@intel.com> * Copyright (C) Allen Kay <allen.m.kay@intel.com> - adapted to xen */#include <xen/irq.h>#include <xen/sched.h>#include <xen/xmalloc.h>#include <xen/domain_page.h>#include <xen/iommu.h>#include <xen/numa.h>#include <xen/time.h>#include <xen/pci.h>#include <xen/pci_regs.h>#include <xen/keyhandler.h>#include <asm/paging.h>#include <asm/msi.h>#include "iommu.h"#include "dmar.h"#include "extern.h"#include "vtd.h"#define domain_iommu_domid(d) ((d)->arch.hvm_domain.hvm_iommu.iommu_domid)static spinlock_t domid_bitmap_lock;    /* protect domain id bitmap */static int domid_bitmap_size;           /* domain id bitmap size in bits */static unsigned long *domid_bitmap;     /* iommu domain id bitmap */static void setup_dom0_devices(struct domain *d);static void setup_dom0_rmrr(struct domain *d);#define DID_FIELD_WIDTH 16#define DID_HIGH_OFFSET 8static void context_set_domain_id(struct context_entry *context,                                  struct domain *d){    unsigned long flags;    domid_t iommu_domid = domain_iommu_domid(d);    if ( iommu_domid == 0 )    {        spin_lock_irqsave(&domid_bitmap_lock, flags);        iommu_domid = find_first_zero_bit(domid_bitmap, domid_bitmap_size);        set_bit(iommu_domid, domid_bitmap);        spin_unlock_irqrestore(&domid_bitmap_lock, flags);        d->arch.hvm_domain.hvm_iommu.iommu_domid = iommu_domid;    }    context->hi &= (1 << DID_HIGH_OFFSET) - 1;    context->hi |= iommu_domid << DID_HIGH_OFFSET;}static void iommu_domid_release(struct domain *d){    domid_t iommu_domid = domain_iommu_domid(d);    if ( iommu_domid != 0 )    {        d->arch.hvm_domain.hvm_iommu.iommu_domid = 0;        clear_bit(iommu_domid, domid_bitmap);    }}static struct intel_iommu *alloc_intel_iommu(void){    struct intel_iommu *intel;    intel = xmalloc(struct intel_iommu);    if ( intel == NULL )        return NULL;    memset(intel, 0, sizeof(struct intel_iommu));    spin_lock_init(&intel->qi_ctrl.qinval_lock);    spin_lock_init(&intel->qi_ctrl.qinval_poll_lock);    spin_lock_init(&intel->ir_ctrl.iremap_lock);    return intel;}static void free_intel_iommu(struct intel_iommu *intel){    xfree(intel);}struct qi_ctrl *iommu_qi_ctrl(struct iommu *iommu){    return iommu ? &iommu->intel->qi_ctrl : NULL;}struct ir_ctrl *iommu_ir_ctrl(struct iommu *iommu){    return iommu ? &iommu->intel->ir_ctrl : NULL;}struct iommu_flush *iommu_get_flush(struct iommu *iommu){    return iommu ? &iommu->intel->flush : NULL;}static unsigned int clflush_size;static int iommus_incoherent;static void __iommu_flush_cache(void *addr, int size){    int i;    if ( !iommus_incoherent )        return;    for ( i = 0; i < size; i += clflush_size )        clflush((char *)addr + i);}void iommu_flush_cache_entry(void *addr){    __iommu_flush_cache(addr, 8);}void iommu_flush_cache_page(void *addr){    __iommu_flush_cache(addr, PAGE_SIZE_4K);}int nr_iommus;/* context entry handling */static u64 bus_to_context_maddr(struct iommu *iommu, u8 bus){    struct root_entry *root, *root_entries;    unsigned long flags;    u64 maddr;    spin_lock_irqsave(&iommu->lock, flags);    root_entries = (struct root_entry *)map_vtd_domain_page(iommu->root_maddr);    root = &root_entries[bus];    if ( !root_present(*root) )    {        maddr = alloc_pgtable_maddr();        if ( maddr == 0 )        {            spin_unlock_irqrestore(&iommu->lock, flags);            return 0;        }        set_root_value(*root, maddr);        set_root_present(*root);        iommu_flush_cache_entry(root);    }    maddr = (u64) get_context_addr(*root);    unmap_vtd_domain_page(root_entries);    spin_unlock_irqrestore(&iommu->lock, flags);    return maddr;}static int device_context_mapped(struct iommu *iommu, u8 bus, u8 devfn){    struct root_entry *root, *root_entries;    struct context_entry *context;    u64 context_maddr;    int ret;    unsigned long flags;    spin_lock_irqsave(&iommu->lock, flags);    root_entries = (struct root_entry *)map_vtd_domain_page(iommu->root_maddr);    root = &root_entries[bus];    if ( !root_present(*root) )    {        ret = 0;        goto out;    }    context_maddr = get_context_addr(*root);    context = (struct context_entry *)map_vtd_domain_page(context_maddr);    ret = context_present(context[devfn]);    unmap_vtd_domain_page(context); out:    unmap_vtd_domain_page(root_entries);    spin_unlock_irqrestore(&iommu->lock, flags);    return ret;}static u64 addr_to_dma_page_maddr(struct domain *domain, u64 addr, int alloc){    struct hvm_iommu *hd = domain_hvm_iommu(domain);    int addr_width = agaw_to_width(hd->agaw);    struct dma_pte *parent, *pte = NULL;    int level = agaw_to_level(hd->agaw);    int offset;    unsigned long flags;    u64 pte_maddr = 0, maddr;    u64 *vaddr = NULL;    addr &= (((u64)1) << addr_width) - 1;    spin_lock_irqsave(&hd->mapping_lock, flags);    if ( hd->pgd_maddr == 0 )        if ( !alloc || ((hd->pgd_maddr = alloc_pgtable_maddr()) == 0) )            goto out;    parent = (struct dma_pte *)map_vtd_domain_page(hd->pgd_maddr);    while ( level > 1 )    {        offset = address_level_offset(addr, level);        pte = &parent[offset];        if ( dma_pte_addr(*pte) == 0 )        {            if ( !alloc )                break;            maddr = alloc_pgtable_maddr();            dma_set_pte_addr(*pte, maddr);            vaddr = map_vtd_domain_page(maddr);            if ( !vaddr )                break;            /*             * high level table always sets r/w, last level             * page table control read/write             */            dma_set_pte_readable(*pte);            dma_set_pte_writable(*pte);            iommu_flush_cache_entry(pte);        }        else        {            vaddr = map_vtd_domain_page(pte->val);            if ( !vaddr )                break;        }        if ( level == 2 )        {            pte_maddr = pte->val & PAGE_MASK_4K;            unmap_vtd_domain_page(vaddr);            break;        }        unmap_vtd_domain_page(parent);        parent = (struct dma_pte *)vaddr;        vaddr = NULL;        level--;    }    unmap_vtd_domain_page(parent); out:    spin_unlock_irqrestore(&hd->mapping_lock, flags);    return pte_maddr;}static void iommu_flush_write_buffer(struct iommu *iommu){    u32 val;    unsigned long flag;    s_time_t start_time;    if ( !cap_rwbf(iommu->cap) )        return;    val = iommu->gcmd | DMA_GCMD_WBF;    spin_lock_irqsave(&iommu->register_lock, flag);    dmar_writel(iommu->reg, DMAR_GCMD_REG, val);    /* Make sure hardware complete it */    start_time = NOW();    for ( ; ; )    {        val = dmar_readl(iommu->reg, DMAR_GSTS_REG);        if ( !(val & DMA_GSTS_WBFS) )            break;        if ( NOW() > start_time + DMAR_OPERATION_TIMEOUT )            panic("%s: DMAR hardware is malfunctional,"                  " please disable IOMMU\n", __func__);        cpu_relax();    }    spin_unlock_irqrestore(&iommu->register_lock, flag);}/* return value determine if we need a write buffer flush */static int flush_context_reg(    void *_iommu,    u16 did, u16 source_id, u8 function_mask, u64 type,    int non_present_entry_flush){    struct iommu *iommu = (struct iommu *) _iommu;    u64 val = 0;    unsigned long flag;    s_time_t start_time;    /*     * In the non-present entry flush case, if hardware doesn't cache     * non-present entry we do nothing and if hardware cache non-present     * entry, we flush entries of domain 0 (the domain id is used to cache     * any non-present entries)     */    if ( non_present_entry_flush )    {        if ( !cap_caching_mode(iommu->cap) )            return 1;        else            did = 0;    }    /* use register invalidation */    switch ( type )    {    case DMA_CCMD_GLOBAL_INVL:        val = DMA_CCMD_GLOBAL_INVL;        break;    case DMA_CCMD_DOMAIN_INVL:        val = DMA_CCMD_DOMAIN_INVL|DMA_CCMD_DID(did);        break;    case DMA_CCMD_DEVICE_INVL:        val = DMA_CCMD_DEVICE_INVL|DMA_CCMD_DID(did)            |DMA_CCMD_SID(source_id)|DMA_CCMD_FM(function_mask);        break;    default:        BUG();    }    val |= DMA_CCMD_ICC;    spin_lock_irqsave(&iommu->register_lock, flag);    dmar_writeq(iommu->reg, DMAR_CCMD_REG, val);    /* Make sure hardware complete it */    start_time = NOW();    for ( ; ; )    {        val = dmar_readq(iommu->reg, DMAR_CCMD_REG);        if ( !(val & DMA_CCMD_ICC) )            break;        if ( NOW() > start_time + DMAR_OPERATION_TIMEOUT )            panic("%s: DMAR hardware is malfunctional,"                  " please disable IOMMU\n", __func__);        cpu_relax();    }    spin_unlock_irqrestore(&iommu->register_lock, flag);    /* flush context entry will implictly flush write buffer */    return 0;}static int inline iommu_flush_context_global(    struct iommu *iommu, int non_present_entry_flush){    struct iommu_flush *flush = iommu_get_flush(iommu);    return flush->context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL,                                 non_present_entry_flush);}static int inline iommu_flush_context_domain(    struct iommu *iommu, u16 did, int non_present_entry_flush){    struct iommu_flush *flush = iommu_get_flush(iommu);    return flush->context(iommu, did, 0, 0, DMA_CCMD_DOMAIN_INVL,                                 non_present_entry_flush);}static int inline iommu_flush_context_device(    struct iommu *iommu, u16 did, u16 source_id,    u8 function_mask, int non_present_entry_flush){    struct iommu_flush *flush = iommu_get_flush(iommu);    return flush->context(iommu, did, source_id, function_mask,                                 DMA_CCMD_DEVICE_INVL,                                 non_present_entry_flush);}/* return value determine if we need a write buffer flush */static int flush_iotlb_reg(void *_iommu, u16 did,                               u64 addr, unsigned int size_order, u64 type,                               int non_present_entry_flush){    struct iommu *iommu = (struct iommu *) _iommu;    int tlb_offset = ecap_iotlb_offset(iommu->ecap);    u64 val = 0, val_iva = 0;    unsigned long flag;    s_time_t start_time;    /*     * In the non-present entry flush case, if hardware doesn't cache     * non-present entry we do nothing and if hardware cache non-present     * entry, we flush entries of domain 0 (the domain id is used to cache     * any non-present entries)     */    if ( non_present_entry_flush )    {        if ( !cap_caching_mode(iommu->cap) )            return 1;        else            did = 0;    }    /* use register invalidation */    switch ( type )    {    case DMA_TLB_GLOBAL_FLUSH:        /* global flush doesn't need set IVA_REG */        val = DMA_TLB_GLOBAL_FLUSH|DMA_TLB_IVT;        break;    case DMA_TLB_DSI_FLUSH:        val = DMA_TLB_DSI_FLUSH|DMA_TLB_IVT|DMA_TLB_DID(did);        break;    case DMA_TLB_PSI_FLUSH:        val = DMA_TLB_PSI_FLUSH|DMA_TLB_IVT|DMA_TLB_DID(did);        /* Note: always flush non-leaf currently */        val_iva = size_order | addr;        break;    default:        BUG();    }    /* Note: set drain read/write */    if ( cap_read_drain(iommu->cap) )        val |= DMA_TLB_READ_DRAIN;    if ( cap_write_drain(iommu->cap) )        val |= DMA_TLB_WRITE_DRAIN;    spin_lock_irqsave(&iommu->register_lock, flag);    /* Note: Only uses first TLB reg currently */    if ( val_iva )        dmar_writeq(iommu->reg, tlb_offset, val_iva);    dmar_writeq(iommu->reg, tlb_offset + 8, val);    /* Make sure hardware complete it */    start_time = NOW();    for ( ; ; )    {        val = dmar_readq(iommu->reg, tlb_offset + 8);        if ( !(val & DMA_TLB_IVT) )            break;        if ( NOW() > start_time + DMAR_OPERATION_TIMEOUT )            panic("%s: DMAR hardware is malfunctional,"                  " please disable IOMMU\n", __func__);        cpu_relax();    }    spin_unlock_irqrestore(&iommu->register_lock, flag);    /* check IOTLB invalidation granularity */    if ( DMA_TLB_IAIG(val) == 0 )        dprintk(XENLOG_ERR VTDPREFIX, "IOMMU: flush IOTLB failed\n");    if ( DMA_TLB_IAIG(val) != DMA_TLB_IIRG(type) )        dprintk(XENLOG_INFO VTDPREFIX,                "IOMMU: tlb flush request %x, actual %x\n",               (u32)DMA_TLB_IIRG(type), (u32)DMA_TLB_IAIG(val));    /* flush context entry will implictly flush write buffer */    return 0;}static int inline iommu_flush_iotlb_global(struct iommu *iommu,                                           int non_present_entry_flush){    struct iommu_flush *flush = iommu_get_flush(iommu);    return flush->iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH,                               non_present_entry_flush);}static int inline iommu_flush_iotlb_dsi(struct iommu *iommu, u16 did,                                        int non_present_entry_flush){    struct iommu_flush *flush = iommu_get_flush(iommu);    return flush->iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH,                               non_present_entry_flush);}static int inline get_alignment(u64 base, unsigned int size){    int t = 0;    u64 end;    end = base + size - 1;    while ( base != end )    {        t++;        base >>= 1;        end >>= 1;    }    return t;}static int inline iommu_flush_iotlb_psi(    struct iommu *iommu, u16 did,

⌨️ 快捷键说明

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