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 <linux/pci-acpi.h>
  23#include <xen/xen.h>
  24#include <xen/interface/physdev.h>
  25#include <xen/interface/xen.h>
  26
  27#include <asm/xen/hypervisor.h>
  28#include <asm/xen/hypercall.h>
  29#include "../pci/pci.h"
  30#ifdef CONFIG_PCI_MMCONFIG
  31#include <asm/pci_x86.h>
  32#endif
  33
  34static bool __read_mostly pci_seg_supported = true;
  35
  36static int xen_add_device(struct device *dev)
  37{
  38        int r;
  39        struct pci_dev *pci_dev = to_pci_dev(dev);
  40#ifdef CONFIG_PCI_IOV
  41        struct pci_dev *physfn = pci_dev->physfn;
  42#endif
  43
  44        if (pci_seg_supported) {
  45                struct {
  46                        struct physdev_pci_device_add add;
  47                        uint32_t pxm;
  48                } add_ext = {
  49                        .add.seg = pci_domain_nr(pci_dev->bus),
  50                        .add.bus = pci_dev->bus->number,
  51                        .add.devfn = pci_dev->devfn
  52                };
  53                struct physdev_pci_device_add *add = &add_ext.add;
  54
  55#ifdef CONFIG_ACPI
  56                acpi_handle handle;
  57#endif
  58
  59#ifdef CONFIG_PCI_IOV
  60                if (pci_dev->is_virtfn) {
  61                        add->flags = XEN_PCI_DEV_VIRTFN;
  62                        add->physfn.bus = physfn->bus->number;
  63                        add->physfn.devfn = physfn->devfn;
  64                } else
  65#endif
  66                if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn))
  67                        add->flags = XEN_PCI_DEV_EXTFN;
  68
  69#ifdef CONFIG_ACPI
  70                handle = ACPI_HANDLE(&pci_dev->dev);
  71#ifdef CONFIG_PCI_IOV
  72                if (!handle && pci_dev->is_virtfn)
  73                        handle = ACPI_HANDLE(physfn->bus->bridge);
  74#endif
  75                if (!handle) {
  76                        /*
  77                         * This device was not listed in the ACPI name space at
  78                         * all. Try to get acpi handle of parent pci bus.
  79                         */
  80                        struct pci_bus *pbus;
  81                        for (pbus = pci_dev->bus; pbus; pbus = pbus->parent) {
  82                                handle = acpi_pci_get_bridge_handle(pbus);
  83                                if (handle)
  84                                        break;
  85                        }
  86                }
  87                if (handle) {
  88                        acpi_status status;
  89
  90                        do {
  91                                unsigned long long pxm;
  92
  93                                status = acpi_evaluate_integer(handle, "_PXM",
  94                                                               NULL, &pxm);
  95                                if (ACPI_SUCCESS(status)) {
  96                                        add->optarr[0] = pxm;
  97                                        add->flags |= XEN_PCI_DEV_PXM;
  98                                        break;
  99                                }
 100                                status = acpi_get_parent(handle, &handle);
 101                        } while (ACPI_SUCCESS(status));
 102                }
 103#endif /* CONFIG_ACPI */
 104
 105                r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_add, add);
 106                if (r != -ENOSYS)
 107                        return r;
 108                pci_seg_supported = false;
 109        }
 110
 111        if (pci_domain_nr(pci_dev->bus))
 112                r = -ENOSYS;
 113#ifdef CONFIG_PCI_IOV
 114        else if (pci_dev->is_virtfn) {
 115                struct physdev_manage_pci_ext manage_pci_ext = {
 116                        .bus            = pci_dev->bus->number,
 117                        .devfn          = pci_dev->devfn,
 118                        .is_virtfn      = 1,
 119                        .physfn.bus     = physfn->bus->number,
 120                        .physfn.devfn   = physfn->devfn,
 121                };
 122
 123                r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
 124                        &manage_pci_ext);
 125        }
 126#endif
 127        else if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) {
 128                struct physdev_manage_pci_ext manage_pci_ext = {
 129                        .bus            = pci_dev->bus->number,
 130                        .devfn          = pci_dev->devfn,
 131                        .is_extfn       = 1,
 132                };
 133
 134                r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
 135                        &manage_pci_ext);
 136        } else {
 137                struct physdev_manage_pci manage_pci = {
 138                        .bus    = pci_dev->bus->number,
 139                        .devfn  = pci_dev->devfn,
 140                };
 141
 142                r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add,
 143                        &manage_pci);
 144        }
 145
 146        return r;
 147}
 148
 149static int xen_remove_device(struct device *dev)
 150{
 151        int r;
 152        struct pci_dev *pci_dev = to_pci_dev(dev);
 153
 154        if (pci_seg_supported) {
 155                struct physdev_pci_device device = {
 156                        .seg = pci_domain_nr(pci_dev->bus),
 157                        .bus = pci_dev->bus->number,
 158                        .devfn = pci_dev->devfn
 159                };
 160
 161                r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_remove,
 162                                          &device);
 163        } else if (pci_domain_nr(pci_dev->bus))
 164                r = -ENOSYS;
 165        else {
 166                struct physdev_manage_pci manage_pci = {
 167                        .bus = pci_dev->bus->number,
 168                        .devfn = pci_dev->devfn
 169                };
 170
 171                r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove,
 172                                          &manage_pci);
 173        }
 174
 175        return r;
 176}
 177
 178static int xen_pci_notifier(struct notifier_block *nb,
 179                            unsigned long action, void *data)
 180{
 181        struct device *dev = data;
 182        int r = 0;
 183
 184        switch (action) {
 185        case BUS_NOTIFY_ADD_DEVICE:
 186                r = xen_add_device(dev);
 187                break;
 188        case BUS_NOTIFY_DEL_DEVICE:
 189                r = xen_remove_device(dev);
 190                break;
 191        default:
 192                return NOTIFY_DONE;
 193        }
 194        if (r)
 195                dev_err(dev, "Failed to %s - passthrough or MSI/MSI-X might fail!\n",
 196                        action == BUS_NOTIFY_ADD_DEVICE ? "add" :
 197                        (action == BUS_NOTIFY_DEL_DEVICE ? "delete" : "?"));
 198        return NOTIFY_OK;
 199}
 200
 201static struct notifier_block device_nb = {
 202        .notifier_call = xen_pci_notifier,
 203};
 204
 205static int __init register_xen_pci_notifier(void)
 206{
 207        if (!xen_initial_domain())
 208                return 0;
 209
 210        return bus_register_notifier(&pci_bus_type, &device_nb);
 211}
 212
 213arch_initcall(register_xen_pci_notifier);
 214
 215#ifdef CONFIG_PCI_MMCONFIG
 216static int __init xen_mcfg_late(void)
 217{
 218        struct pci_mmcfg_region *cfg;
 219        int rc;
 220
 221        if (!xen_initial_domain())
 222                return 0;
 223
 224        if ((pci_probe & PCI_PROBE_MMCONF) == 0)
 225                return 0;
 226
 227        if (list_empty(&pci_mmcfg_list))
 228                return 0;
 229
 230        /* Check whether they are in the right area. */
 231        list_for_each_entry(cfg, &pci_mmcfg_list, list) {
 232                struct physdev_pci_mmcfg_reserved r;
 233
 234                r.address = cfg->address;
 235                r.segment = cfg->segment;
 236                r.start_bus = cfg->start_bus;
 237                r.end_bus = cfg->end_bus;
 238                r.flags = XEN_PCI_MMCFG_RESERVED;
 239
 240                rc = HYPERVISOR_physdev_op(PHYSDEVOP_pci_mmcfg_reserved, &r);
 241                switch (rc) {
 242                case 0:
 243                case -ENOSYS:
 244                        continue;
 245
 246                default:
 247                        pr_warn("Failed to report MMCONFIG reservation"
 248                                " state for %s to hypervisor"
 249                                " (%d)\n",
 250                                cfg->name, rc);
 251                }
 252        }
 253        return 0;
 254}
 255/*
 256 * Needs to be done after acpi_init which are subsys_initcall.
 257 */
 258subsys_initcall_sync(xen_mcfg_late);
 259#endif
 260