| /* | |
| * 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); |