linux/drivers/xen/pci.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2009, Intel Corporation.
   3 *
   4 * This program is free software; you can redistribute it and/or modify it
   5 * under the terms and conditions of the GNU General Public License,
   6 * version 2, as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope it will be useful, but WITHOUT
   9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  11 * more details.
  12 *
  13 * You should have received a copy of the GNU General Public License along with
  14 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
  15 * Place - Suite 330, Boston, MA 02111-1307 USA.
  16 *
  17 * Author: Weidong Han <weidong.han@intel.com>
  18 */
  19
  20#include <linux/pci.h>
  21#include <linux/acpi.h>
  22#include <xen/xen.h>
  23#include <xen/interface/physdev.h>
  24#include <xen/interface/xen.h>
  25
  26#include <asm/xen/hypervisor.h>
  27#include <asm/xen/hypercall.h>
  28#include "../pci/pci.h"
  29
  30static bool __read_mostly pci_seg_supported = true;
  31
  32static int xen_add_device(struct device *dev)
  33{
  34        int r;
  35        struct pci_dev *pci_dev = to_pci_dev(dev);
  36#ifdef CONFIG_PCI_IOV
  37        struct pci_dev *physfn = pci_dev->physfn;
  38#endif
  39
  40        if (pci_seg_supported) {
  41                struct physdev_pci_device_add add = {
  42                        .seg = pci_domain_nr(pci_dev->bus),
  43                        .bus = pci_dev->bus->number,
  44                        .devfn = pci_dev->devfn
  45                };
  46#ifdef CONFIG_ACPI
  47                acpi_handle handle;
  48#endif
  49
  50#ifdef CONFIG_PCI_IOV
  51                if (pci_dev->is_virtfn) {
  52                        add.flags = XEN_PCI_DEV_VIRTFN;
  53                        add.physfn.bus = physfn->bus->number;
  54                        add.physfn.devfn = physfn->devfn;
  55                } else
  56#endif
  57                if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn))
  58                        add.flags = XEN_PCI_DEV_EXTFN;
  59
  60#ifdef CONFIG_ACPI
  61                handle = DEVICE_ACPI_HANDLE(&pci_dev->dev);
  62                if (!handle && pci_dev->bus->bridge)
  63                        handle = DEVICE_ACPI_HANDLE(pci_dev->bus->bridge);
  64#ifdef CONFIG_PCI_IOV
  65                if (!handle && pci_dev->is_virtfn)
  66                        handle = DEVICE_ACPI_HANDLE(physfn->bus->bridge);
  67#endif
  68                if (handle) {
  69                        acpi_status status;
  70
  71                        do {
  72                                unsigned long long pxm;
  73
  74                                status = acpi_evaluate_integer(handle, "_PXM",
  75                                                               NULL, &pxm);
  76                                if (ACPI_SUCCESS(status)) {
  77                                        add.optarr[0] = pxm;
  78                                        add.flags |= XEN_PCI_DEV_PXM;
  79                                        break;
  80                                }
  81                                status = acpi_get_parent(handle, &handle);
  82                        } while (ACPI_SUCCESS(status));
  83                }
  84#endif /* CONFIG_ACPI */
  85
  86                r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_add, &add);
  87                if (r != -ENOSYS)
  88                        return r;
  89                pci_seg_supported = false;
  90        }
  91
  92        if (pci_domain_nr(pci_dev->bus))
  93                r = -ENOSYS;
  94#ifdef CONFIG_PCI_IOV
  95        else if (pci_dev->is_virtfn) {
  96                struct physdev_manage_pci_ext manage_pci_ext = {
  97                        .bus            = pci_dev->bus->number,
  98                        .devfn          = pci_dev->devfn,
  99                        .is_virtfn      = 1,
 100                        .physfn.bus     = physfn->bus->number,
 101                        .physfn.devfn   = physfn->devfn,
 102                };
 103
 104                r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
 105                        &manage_pci_ext);
 106        }
 107#endif
 108        else if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) {
 109                struct physdev_manage_pci_ext manage_pci_ext = {
 110                        .bus            = pci_dev->bus->number,
 111                        .devfn          = pci_dev->devfn,
 112                        .is_extfn       = 1,
 113                };
 114
 115                r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
 116                        &manage_pci_ext);
 117        } else {
 118                struct physdev_manage_pci manage_pci = {
 119                        .bus    = pci_dev->bus->number,
 120                        .devfn  = pci_dev->devfn,
 121                };
 122
 123                r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add,
 124                        &manage_pci);
 125        }
 126
 127        return r;
 128}
 129
 130static int xen_remove_device(struct device *dev)
 131{
 132        int r;
 133        struct pci_dev *pci_dev = to_pci_dev(dev);
 134
 135        if (pci_seg_supported) {
 136                struct physdev_pci_device device = {
 137                        .seg = pci_domain_nr(pci_dev->bus),
 138                        .bus = pci_dev->bus->number,
 139                        .devfn = pci_dev->devfn
 140                };
 141
 142                r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_remove,
 143                                          &device);
 144        } else if (pci_domain_nr(pci_dev->bus))
 145                r = -ENOSYS;
 146        else {
 147                struct physdev_manage_pci manage_pci = {
 148                        .bus = pci_dev->bus->number,
 149                        .devfn = pci_dev->devfn
 150                };
 151
 152                r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove,
 153                                          &manage_pci);
 154        }
 155
 156        return r;
 157}
 158
 159static int xen_pci_notifier(struct notifier_block *nb,
 160                            unsigned long action, void *data)
 161{
 162        struct device *dev = data;
 163        int r = 0;
 164
 165        switch (action) {
 166        case BUS_NOTIFY_ADD_DEVICE:
 167                r = xen_add_device(dev);
 168                break;
 169        case BUS_NOTIFY_DEL_DEVICE:
 170                r = xen_remove_device(dev);
 171                break;
 172        default:
 173                return NOTIFY_DONE;
 174        }
 175        if (r)
 176                dev_err(dev, "Failed to %s - passthrough or MSI/MSI-X might fail!\n",
 177                        action == BUS_NOTIFY_ADD_DEVICE ? "add" :
 178                        (action == BUS_NOTIFY_DEL_DEVICE ? "delete" : "?"));
 179        return NOTIFY_OK;
 180}
 181
 182static struct notifier_block device_nb = {
 183        .notifier_call = xen_pci_notifier,
 184};
 185
 186static int __init register_xen_pci_notifier(void)
 187{
 188        if (!xen_initial_domain())
 189                return 0;
 190
 191        return bus_register_notifier(&pci_bus_type, &device_nb);
 192}
 193
 194arch_initcall(register_xen_pci_notifier);
 195