PCI: don't allow guest assignment of devices used by Xen This covers the devices used for the console and the AMD IOMMU ones (as would be any others that might get passed to pci_ro_device()). Boot video device determination cloned from similar Linux logic. Signed-off-by: Jan Beulich --- Note that to apply cleanly, this has to go on top of the serial console improvement series posted earlier. --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -311,9 +311,12 @@ void __init arch_init_memory(void) * Initialise our DOMID_XEN domain. * Any Xen-heap pages that we will allow to be mapped will have * their domain field set to dom_xen. + * Hidden PCI devices will also be associated with this domain + * (but be [partly] controlled by Dom0 nevertheless). */ dom_xen = domain_create(DOMID_XEN, DOMCRF_dummy, 0); BUG_ON(IS_ERR(dom_xen)); + INIT_LIST_HEAD(&dom_xen->arch.pdev_list); /* * Initialise our DOMID_IO domain. --- a/xen/drivers/char/ehci-dbgp.c +++ b/xen/drivers/char/ehci-dbgp.c @@ -1364,6 +1364,8 @@ static void __init ehci_dbgp_init_postir init_timer(&dbgp->timer, ehci_dbgp_poll, port, 0); ehci_dbgp_setup_postirq(dbgp); + + pci_hide_device(dbgp->bus, PCI_DEVFN(dbgp->slot, dbgp->func)); } static int ehci_dbgp_check_release(struct ehci_dbgp *dbgp) --- a/xen/drivers/char/ns16550.c +++ b/xen/drivers/char/ns16550.c @@ -334,6 +334,10 @@ static void __init ns16550_init_postirq( } ns16550_setup_postirq(uart); + + if ( uart->bar || uart->ps_bdf_enable ) + pci_hide_device(uart->ps_bdf[0], PCI_DEVFN(uart->ps_bdf[1], + uart->ps_bdf[2])); } static void ns16550_suspend(struct serial_port *port) --- a/xen/drivers/passthrough/iommu.c +++ b/xen/drivers/passthrough/iommu.c @@ -208,7 +208,7 @@ static int device_assigned(u16 seg, u8 b pdev = pci_get_pdev_by_domain(dom0, seg, bus, devfn); spin_unlock(&pcidevs_lock); - return pdev ? 0 : -1; + return pdev ? 0 : -EBUSY; } static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) @@ -614,7 +614,8 @@ int iommu_do_domctl( bus = (domctl->u.assign_device.machine_sbdf >> 8) & 0xff; devfn = domctl->u.assign_device.machine_sbdf & 0xff; - ret = assign_device(d, seg, bus, devfn); + ret = device_assigned(seg, bus, devfn) ?: + assign_device(d, seg, bus, devfn); if ( ret ) printk(XENLOG_G_ERR "XEN_DOMCTL_assign_device: " "assign %04x:%02x:%02x.%u to dom%d failed (%d)\n", --- a/xen/drivers/passthrough/pci.c +++ b/xen/drivers/passthrough/pci.c @@ -208,6 +208,31 @@ static void free_pdev(struct pci_seg *ps xfree(pdev); } +static void _pci_hide_device(struct pci_dev *pdev) +{ + if ( pdev->domain ) + return; + pdev->domain = dom_xen; + list_add(&pdev->domain_list, &dom_xen->arch.pdev_list); +} + +int __init pci_hide_device(int bus, int devfn) +{ + struct pci_dev *pdev; + int rc = -ENOMEM; + + spin_lock(&pcidevs_lock); + pdev = alloc_pdev(get_pseg(0), bus, devfn); + if ( pdev ) + { + _pci_hide_device(pdev); + rc = 0; + } + spin_unlock(&pcidevs_lock); + + return rc; +} + int __init pci_ro_device(int seg, int bus, int devfn) { struct pci_seg *pseg = alloc_pseg(seg); @@ -231,6 +256,7 @@ int __init pci_ro_device(int seg, int bu __set_bit(PCI_BDF2(bus, devfn), pseg->ro_map); arch_pci_ro_device(seg, PCI_BDF2(bus, devfn)); + _pci_hide_device(pdev); return 0; } @@ -718,9 +744,22 @@ static int __init _setup_dom0_pci_device if ( !pdev ) continue; - pdev->domain = ctxt->d; - list_add(&pdev->domain_list, &ctxt->d->arch.pdev_list); - ctxt->handler(pdev); + if ( !pdev->domain ) + { + pdev->domain = ctxt->d; + list_add(&pdev->domain_list, &ctxt->d->arch.pdev_list); + ctxt->handler(pdev); + } + else if ( pdev->domain == dom_xen ) + { + pdev->domain = ctxt->d; + ctxt->handler(pdev); + pdev->domain = dom_xen; + } + else if ( pdev->domain != ctxt->d ) + printk(XENLOG_WARNING "Dom%d owning %04x:%02x:%02x.%u?\n", + pdev->domain->domain_id, pseg->nr, bus, + PCI_SLOT(devfn), PCI_FUNC(devfn)); } } --- a/xen/drivers/video/vga.c +++ b/xen/drivers/video/vga.c @@ -9,6 +9,7 @@ #include #include #include +#include #include /* Filled in by arch boot code. */ @@ -106,6 +107,61 @@ void __init vga_endboot(void) if ( !vgacon_keep ) vga_puts = vga_noop_puts; + else + { + int bus, devfn; + + for ( bus = 0; bus < 256; ++bus ) + for ( devfn = 0; devfn < 256; ++devfn ) + { + const struct pci_dev *pdev; + u8 b = bus, df = devfn, sb; + + spin_lock(&pcidevs_lock); + pdev = pci_get_pdev(0, bus, devfn); + spin_unlock(&pcidevs_lock); + + if ( !pdev || + pci_conf_read16(0, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), + PCI_CLASS_DEVICE) != 0x0300 || + !(pci_conf_read16(0, bus, PCI_SLOT(devfn), + PCI_FUNC(devfn), PCI_COMMAND) & + (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) ) + continue; + + while ( b ) + { + switch ( find_upstream_bridge(0, &b, &df, &sb) ) + { + case 0: + b = 0; + break; + case 1: + switch ( pci_conf_read8(0, b, PCI_SLOT(df), + PCI_FUNC(df), + PCI_HEADER_TYPE) ) + { + case PCI_HEADER_TYPE_BRIDGE: + case PCI_HEADER_TYPE_CARDBUS: + if ( pci_conf_read16(0, b, PCI_SLOT(df), + PCI_FUNC(df), + PCI_BRIDGE_CONTROL) & + PCI_BRIDGE_CTL_VGA ) + continue; + break; + } + break; + } + break; + } + if ( !b ) + { + printk(XENLOG_INFO "Boot video device %02x:%02x.%u\n", + bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); + pci_hide_device(bus, devfn); + } + } + } switch ( vga_console_info.video_type ) { --- a/xen/include/xen/pci.h +++ b/xen/include/xen/pci.h @@ -103,6 +103,7 @@ int pci_add_device(u16 seg, u8 bus, u8 d int pci_remove_device(u16 seg, u8 bus, u8 devfn); int pci_ro_device(int seg, int bus, int devfn); void arch_pci_ro_device(int seg, int bdf); +int pci_hide_device(int bus, int devfn); struct pci_dev *pci_get_pdev(int seg, int bus, int devfn); struct pci_dev *pci_get_pdev_by_domain( struct domain *, int seg, int bus, int devfn);