physdev.c
来自「xen虚拟机源代码安装包」· C语言 代码 · 共 550 行
C
550 行
#include <xen/config.h>#include <xen/init.h>#include <xen/lib.h>#include <xen/types.h>#include <xen/sched.h>#include <xen/irq.h>#include <xen/event.h>#include <xen/guest_access.h>#include <xen/iocap.h>#include <asm/current.h>#include <asm/msi.h>#include <asm/hypercall.h>#include <public/xen.h>#include <public/physdev.h>#include <xsm/xsm.h>#ifndef COMPATtypedef long ret_t;#endifintioapic_guest_read( unsigned long physbase, unsigned int reg, u32 *pval);intioapic_guest_write( unsigned long physbase, unsigned int reg, u32 pval);extern struct hw_interrupt_type pci_msi_type;static int get_free_pirq(struct domain *d, int type, int index){ int i; if ( d == NULL ) return -EINVAL; ASSERT(spin_is_locked(&d->arch.irq_lock)); if ( type == MAP_PIRQ_TYPE_GSI ) { for ( i = 16; i < NR_PIRQS; i++ ) if ( !d->arch.pirq_vector[i] ) break; if ( i == NR_PIRQS ) return -ENOSPC; } else { for ( i = NR_PIRQS - 1; i >= 16; i-- ) if ( !d->arch.pirq_vector[i] ) break; if ( i == 16 ) return -ENOSPC; } return i;}/* * Caller hold the irq_lock */static int map_domain_pirq(struct domain *d, int pirq, int vector, struct physdev_map_pirq *map){ int ret = 0; int old_vector, old_pirq; struct msi_info msi; if ( d == NULL ) return -EINVAL; ASSERT(spin_is_locked(&d->arch.irq_lock)); if ( !IS_PRIV(current->domain) ) return -EPERM; if ( pirq < 0 || pirq >= NR_PIRQS || vector < 0 || vector >= NR_VECTORS ) { gdprintk(XENLOG_G_ERR, "invalid pirq %x or vector %x\n", pirq, vector); return -EINVAL; } old_vector = d->arch.pirq_vector[pirq]; old_pirq = d->arch.vector_pirq[vector]; if ( (old_vector && (old_vector != vector) ) || (old_pirq && (old_pirq != pirq)) ) { gdprintk(XENLOG_G_ERR, "remap pirq %x vector %x while not unmap\n", pirq, vector); ret = -EINVAL; goto done; } ret = irq_permit_access(d, pirq); if ( ret ) { gdprintk(XENLOG_G_ERR, "add irq permit access %x failed\n", pirq); ret = -EINVAL; goto done; } if ( map && MAP_PIRQ_TYPE_MSI == map->type ) { irq_desc_t *desc; unsigned long flags; desc = &irq_desc[vector]; spin_lock_irqsave(&desc->lock, flags); if ( desc->handler != &no_irq_type ) gdprintk(XENLOG_G_ERR, "Map vector %x to msi while it is in use\n", vector); desc->handler = &pci_msi_type; msi.bus = map->bus; msi.devfn = map->devfn; msi.entry_nr = map->entry_nr; msi.table_base = map->table_base; msi.vector = vector; ret = pci_enable_msi(&msi); spin_unlock_irqrestore(&desc->lock, flags); if ( ret ) goto done; } d->arch.pirq_vector[pirq] = vector; d->arch.vector_pirq[vector] = pirq;done: return ret;}/* * The pirq should has been unbound before this call */static int unmap_domain_pirq(struct domain *d, int pirq){ int ret = 0; int vector; if ( d == NULL || pirq < 0 || pirq >= NR_PIRQS ) return -EINVAL; if ( !IS_PRIV(current->domain) ) return -EINVAL; ASSERT(spin_is_locked(&d->arch.irq_lock)); vector = d->arch.pirq_vector[pirq]; if ( !vector ) { gdprintk(XENLOG_G_ERR, "domain %X: pirq %x not mapped still\n", d->domain_id, pirq); ret = -EINVAL; } else { unsigned long flags; irq_desc_t *desc; desc = &irq_desc[vector]; spin_lock_irqsave(&desc->lock, flags); if ( desc->msi_desc ) pci_disable_msi(vector); if ( desc->handler == &pci_msi_type ) { /* MSI is not shared, so should be released already */ BUG_ON(desc->status & IRQ_GUEST); irq_desc[vector].handler = &no_irq_type; } spin_unlock_irqrestore(&desc->lock, flags); d->arch.pirq_vector[pirq] = d->arch.vector_pirq[vector] = 0; } ret = irq_deny_access(d, pirq); if ( ret ) gdprintk(XENLOG_G_ERR, "deny irq %x access failed\n", pirq); return ret;}static int physdev_map_pirq(struct physdev_map_pirq *map){ struct domain *d; int vector, pirq, ret = 0; unsigned long flags; /* if msi_enable is not enabled, map always succeeds */ if ( !msi_enable ) return 0; if ( !IS_PRIV(current->domain) ) return -EPERM; if ( !map ) return -EINVAL; if ( map->domid == DOMID_SELF ) d = rcu_lock_domain(current->domain); else d = rcu_lock_domain_by_id(map->domid); if ( d == NULL ) { ret = -ESRCH; goto free_domain; } switch ( map->type ) { case MAP_PIRQ_TYPE_GSI: if ( map->index >= NR_IRQS ) { ret = -EINVAL; gdprintk(XENLOG_G_ERR, "map invalid irq %x\n", map->index); goto free_domain; } vector = IO_APIC_VECTOR(map->index); if ( !vector ) { ret = -EINVAL; gdprintk(XENLOG_G_ERR, "map irq with no vector %x\n", map->index); goto free_domain; } break; case MAP_PIRQ_TYPE_MSI: vector = map->index; if ( vector == -1 ) vector = assign_irq_vector(AUTO_ASSIGN); if ( vector < 0 || vector >= NR_VECTORS ) { ret = -EINVAL; gdprintk(XENLOG_G_ERR, "map_pirq with wrong vector %x\n", map->index); goto free_domain; } break; default: ret = -EINVAL; gdprintk(XENLOG_G_ERR, "wrong map_pirq type %x\n", map->type); goto free_domain; break; } spin_lock_irqsave(&d->arch.irq_lock, flags); if ( map->pirq == -1 ) { if ( d->arch.vector_pirq[vector] ) { gdprintk(XENLOG_G_ERR, "%x %x mapped already%x\n", map->index, map->pirq, d->arch.vector_pirq[vector]); pirq = d->arch.vector_pirq[vector]; } else { pirq = get_free_pirq(d, map->type, map->index); if ( pirq < 0 ) { ret = pirq; gdprintk(XENLOG_G_ERR, "No free pirq\n"); goto done; } } } else { if ( d->arch.vector_pirq[vector] && d->arch.vector_pirq[vector] != map->pirq ) { gdprintk(XENLOG_G_ERR, "%x conflict with %x\n", map->index, map->pirq); ret = -EEXIST; goto done; } else pirq = map->pirq; } ret = map_domain_pirq(d, pirq, vector, map); if ( !ret ) map->pirq = pirq;done: spin_unlock_irqrestore(&d->arch.irq_lock, flags);free_domain: rcu_unlock_domain(d); return ret;}static int physdev_unmap_pirq(struct physdev_unmap_pirq *unmap){ struct domain *d; unsigned long flags; int ret; if ( !msi_enable ) return 0; if ( !IS_PRIV(current->domain) ) return -EPERM; if ( !unmap ) return -EINVAL; if ( unmap->domid == DOMID_SELF ) d = rcu_lock_domain(current->domain); else d = rcu_lock_domain_by_id(unmap->domid); if ( d == NULL ) { rcu_unlock_domain(d); return -ESRCH; } spin_lock_irqsave(&d->arch.irq_lock, flags); ret = unmap_domain_pirq(d, unmap->pirq); spin_unlock_irqrestore(&d->arch.irq_lock, flags); rcu_unlock_domain(d); return ret;}ret_t do_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg){ int irq; ret_t ret; struct vcpu *v = current; switch ( cmd ) { case PHYSDEVOP_eoi: { struct physdev_eoi eoi; ret = -EFAULT; if ( copy_from_guest(&eoi, arg, 1) != 0 ) break; ret = pirq_guest_eoi(v->domain, eoi.irq); break; } /* Legacy since 0x00030202. */ case PHYSDEVOP_IRQ_UNMASK_NOTIFY: { ret = pirq_guest_unmask(v->domain); break; } case PHYSDEVOP_irq_status_query: { struct physdev_irq_status_query irq_status_query; ret = -EFAULT; if ( copy_from_guest(&irq_status_query, arg, 1) != 0 ) break; irq = irq_status_query.irq; ret = -EINVAL; if ( (irq < 0) || (irq >= NR_IRQS) ) break; irq_status_query.flags = 0; if ( pirq_acktype(v->domain, irq) != 0 ) irq_status_query.flags |= XENIRQSTAT_needs_eoi; if ( pirq_shared(v->domain, irq) ) irq_status_query.flags |= XENIRQSTAT_shared; ret = copy_to_guest(arg, &irq_status_query, 1) ? -EFAULT : 0; break; } case PHYSDEVOP_map_pirq: { struct physdev_map_pirq map; ret = -EFAULT; if ( copy_from_guest(&map, arg, 1) != 0 ) break; ret = physdev_map_pirq(&map); if ( copy_to_guest(arg, &map, 1) != 0 ) ret = -EFAULT; break; } case PHYSDEVOP_unmap_pirq: { struct physdev_unmap_pirq unmap; ret = -EFAULT; if ( copy_from_guest(&unmap, arg, 1) != 0 ) break; ret = physdev_unmap_pirq(&unmap); break; } case PHYSDEVOP_apic_read: { struct physdev_apic apic; ret = -EFAULT; if ( copy_from_guest(&apic, arg, 1) != 0 ) break; ret = -EPERM; if ( !IS_PRIV(v->domain) ) break; ret = xsm_apic(v->domain, cmd); if ( ret ) break; ret = ioapic_guest_read(apic.apic_physbase, apic.reg, &apic.value); if ( copy_to_guest(arg, &apic, 1) != 0 ) ret = -EFAULT; break; } case PHYSDEVOP_apic_write: { struct physdev_apic apic; ret = -EFAULT; if ( copy_from_guest(&apic, arg, 1) != 0 ) break; ret = -EPERM; if ( !IS_PRIV(v->domain) ) break; ret = xsm_apic(v->domain, cmd); if ( ret ) break; ret = ioapic_guest_write(apic.apic_physbase, apic.reg, apic.value); break; } case PHYSDEVOP_alloc_irq_vector: { struct physdev_irq irq_op; unsigned long flags; ret = -EFAULT; if ( copy_from_guest(&irq_op, arg, 1) != 0 ) break; ret = -EPERM; if ( !IS_PRIV(v->domain) ) break; ret = xsm_assign_vector(v->domain, irq_op.irq); if ( ret ) break; irq = irq_op.irq; ret = -EINVAL; if ( ((irq < 0) && (irq != AUTO_ASSIGN)) || (irq >= NR_IRQS) ) break; irq_op.vector = assign_irq_vector(irq); ret = 0; if ( msi_enable ) { spin_lock_irqsave(&dom0->arch.irq_lock, flags); if ( irq != AUTO_ASSIGN ) ret = map_domain_pirq(dom0, irq_op.irq, irq_op.vector, NULL); spin_unlock_irqrestore(&dom0->arch.irq_lock, flags); } if ( copy_to_guest(arg, &irq_op, 1) != 0 ) ret = -EFAULT; break; } case PHYSDEVOP_set_iopl: { struct physdev_set_iopl set_iopl; ret = -EFAULT; if ( copy_from_guest(&set_iopl, arg, 1) != 0 ) break; ret = -EINVAL; if ( set_iopl.iopl > 3 ) break; ret = 0; v->arch.iopl = set_iopl.iopl; break; } case PHYSDEVOP_set_iobitmap: { struct physdev_set_iobitmap set_iobitmap; ret = -EFAULT; if ( copy_from_guest(&set_iobitmap, arg, 1) != 0 ) break; ret = -EINVAL; if ( !guest_handle_okay(set_iobitmap.bitmap, IOBMP_BYTES) || (set_iobitmap.nr_ports > 65536) ) break; ret = 0;#ifndef COMPAT v->arch.iobmp = set_iobitmap.bitmap;#else guest_from_compat_handle(v->arch.iobmp, set_iobitmap.bitmap);#endif v->arch.iobmp_limit = set_iobitmap.nr_ports; break; } case PHYSDEVOP_manage_pci_add: { struct physdev_manage_pci manage_pci; ret = -EPERM; if ( !IS_PRIV(v->domain) ) break; ret = -EFAULT; if ( copy_from_guest(&manage_pci, arg, 1) != 0 ) break; ret = pci_add_device(manage_pci.bus, manage_pci.devfn); break; } case PHYSDEVOP_manage_pci_remove: { struct physdev_manage_pci manage_pci; ret = -EPERM; if ( !IS_PRIV(v->domain) ) break; ret = -EFAULT; if ( copy_from_guest(&manage_pci, arg, 1) != 0 ) break; ret = pci_remove_device(manage_pci.bus, manage_pci.devfn); break; } default: ret = -ENOSYS; break; } return ret;}/* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?