| /* |
| * Copyright (c) 2009, 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. |
| * |
| * Author: Weidong Han <weidong.han@intel.com> |
| */ |
| |
| #include <linux/pci.h> |
| #include <xen/xen.h> |
| #include <xen/interface/physdev.h> |
| #include <xen/interface/xen.h> |
| |
| #include <asm/xen/hypervisor.h> |
| #include <asm/xen/hypercall.h> |
| #include "../pci/pci.h" |
| |
| static int xen_add_device(struct device *dev) |
| { |
| int r; |
| struct pci_dev *pci_dev = to_pci_dev(dev); |
| |
| #ifdef CONFIG_PCI_IOV |
| if (pci_dev->is_virtfn) { |
| struct physdev_manage_pci_ext manage_pci_ext = { |
| .bus = pci_dev->bus->number, |
| .devfn = pci_dev->devfn, |
| .is_virtfn = 1, |
| .physfn.bus = pci_dev->physfn->bus->number, |
| .physfn.devfn = pci_dev->physfn->devfn, |
| }; |
| |
| r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext, |
| &manage_pci_ext); |
| } else |
| #endif |
| if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) { |
| struct physdev_manage_pci_ext manage_pci_ext = { |
| .bus = pci_dev->bus->number, |
| .devfn = pci_dev->devfn, |
| .is_extfn = 1, |
| }; |
| |
| r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext, |
| &manage_pci_ext); |
| } else { |
| struct physdev_manage_pci manage_pci = { |
| .bus = pci_dev->bus->number, |
| .devfn = pci_dev->devfn, |
| }; |
| |
| r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add, |
| &manage_pci); |
| } |
| |
| return r; |
| } |
| |
| static int xen_remove_device(struct device *dev) |
| { |
| int r; |
| struct pci_dev *pci_dev = to_pci_dev(dev); |
| struct physdev_manage_pci manage_pci; |
| |
| manage_pci.bus = pci_dev->bus->number; |
| manage_pci.devfn = pci_dev->devfn; |
| |
| r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove, |
| &manage_pci); |
| |
| return r; |
| } |
| |
| static int xen_pci_notifier(struct notifier_block *nb, |
| unsigned long action, void *data) |
| { |
| struct device *dev = data; |
| int r = 0; |
| |
| switch (action) { |
| case BUS_NOTIFY_ADD_DEVICE: |
| r = xen_add_device(dev); |
| break; |
| case BUS_NOTIFY_DEL_DEVICE: |
| r = xen_remove_device(dev); |
| break; |
| default: |
| break; |
| } |
| |
| return r; |
| } |
| |
| struct notifier_block device_nb = { |
| .notifier_call = xen_pci_notifier, |
| }; |
| |
| static int __init register_xen_pci_notifier(void) |
| { |
| if (!xen_initial_domain()) |
| return 0; |
| |
| return bus_register_notifier(&pci_bus_type, &device_nb); |
| } |
| |
| arch_initcall(register_xen_pci_notifier); |