From e46ea4d44dc0929d9e15dcde5c13b569278970d2 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Tue, 11 Sep 2012 16:01:15 +0200 Subject: [PATCH] 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 Acked-by: Keir Fraser --- xen/arch/x86/mm.c | 3 ++ xen/drivers/char/ehci-dbgp.c | 2 ++ xen/drivers/char/ns16550.c | 4 +++ xen/drivers/passthrough/iommu.c | 5 +-- xen/drivers/passthrough/pci.c | 45 ++++++++++++++++++++++++-- xen/drivers/video/vga.c | 56 +++++++++++++++++++++++++++++++++ xen/include/xen/pci.h | 1 + 7 files changed, 111 insertions(+), 5 deletions(-) diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index 096f202776..99c1784c62 100644 --- 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. diff --git a/xen/drivers/char/ehci-dbgp.c b/xen/drivers/char/ehci-dbgp.c index 47b8405d30..6b7066081d 100644 --- a/xen/drivers/char/ehci-dbgp.c +++ b/xen/drivers/char/ehci-dbgp.c @@ -1364,6 +1364,8 @@ static void __init ehci_dbgp_init_postirq(struct serial_port *port) 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) diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c index 2eb7f6cff7..c7c5b7cc03 100644 --- a/xen/drivers/char/ns16550.c +++ b/xen/drivers/char/ns16550.c @@ -334,6 +334,10 @@ static void __init ns16550_init_postirq(struct serial_port *port) } 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) diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c index 8167f094e9..b4cf16c969 100644 --- a/xen/drivers/passthrough/iommu.c +++ b/xen/drivers/passthrough/iommu.c @@ -208,7 +208,7 @@ static int device_assigned(u16 seg, u8 bus, u8 devfn) 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", diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c index 29d88fd32d..379487ab04 100644 --- a/xen/drivers/passthrough/pci.c +++ b/xen/drivers/passthrough/pci.c @@ -208,6 +208,31 @@ static void free_pdev(struct pci_seg *pseg, struct pci_dev *pdev) 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 bus, int devfn) __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_devices(struct pci_seg *pseg, void *arg) 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)); } } diff --git a/xen/drivers/video/vga.c b/xen/drivers/video/vga.c index 7696946c73..9fabaf4ab4 100644 --- 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 ) { diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h index d302034e30..18b7fb1b11 100644 --- 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 devfn, const struct pci_dev_info *); 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); -- 2.39.5