linux/drivers/xen/xen-pciback/conf_space_capability.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * PCI Backend - Handles the virtual fields found on the capability lists
   4 *               in the configuration space.
   5 *
   6 * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
   7 */
   8
   9#include <linux/kernel.h>
  10#include <linux/pci.h>
  11#include "pciback.h"
  12#include "conf_space.h"
  13
  14static LIST_HEAD(capabilities);
  15struct xen_pcibk_config_capability {
  16        struct list_head cap_list;
  17
  18        int capability;
  19
  20        /* If the device has the capability found above, add these fields */
  21        const struct config_field *fields;
  22};
  23
  24static const struct config_field caplist_header[] = {
  25        {
  26         .offset    = PCI_CAP_LIST_ID,
  27         .size      = 2, /* encompass PCI_CAP_LIST_ID & PCI_CAP_LIST_NEXT */
  28         .u.w.read  = xen_pcibk_read_config_word,
  29         .u.w.write = NULL,
  30        },
  31        {}
  32};
  33
  34static inline void register_capability(struct xen_pcibk_config_capability *cap)
  35{
  36        list_add_tail(&cap->cap_list, &capabilities);
  37}
  38
  39int xen_pcibk_config_capability_add_fields(struct pci_dev *dev)
  40{
  41        int err = 0;
  42        struct xen_pcibk_config_capability *cap;
  43        int cap_offset;
  44
  45        list_for_each_entry(cap, &capabilities, cap_list) {
  46                cap_offset = pci_find_capability(dev, cap->capability);
  47                if (cap_offset) {
  48                        dev_dbg(&dev->dev, "Found capability 0x%x at 0x%x\n",
  49                                cap->capability, cap_offset);
  50
  51                        err = xen_pcibk_config_add_fields_offset(dev,
  52                                                               caplist_header,
  53                                                               cap_offset);
  54                        if (err)
  55                                goto out;
  56                        err = xen_pcibk_config_add_fields_offset(dev,
  57                                                               cap->fields,
  58                                                               cap_offset);
  59                        if (err)
  60                                goto out;
  61                }
  62        }
  63
  64out:
  65        return err;
  66}
  67
  68static int vpd_address_write(struct pci_dev *dev, int offset, u16 value,
  69                             void *data)
  70{
  71        /* Disallow writes to the vital product data */
  72        if (value & PCI_VPD_ADDR_F)
  73                return PCIBIOS_SET_FAILED;
  74        else
  75                return pci_write_config_word(dev, offset, value);
  76}
  77
  78static const struct config_field caplist_vpd[] = {
  79        {
  80         .offset    = PCI_VPD_ADDR,
  81         .size      = 2,
  82         .u.w.read  = xen_pcibk_read_config_word,
  83         .u.w.write = vpd_address_write,
  84         },
  85        {
  86         .offset     = PCI_VPD_DATA,
  87         .size       = 4,
  88         .u.dw.read  = xen_pcibk_read_config_dword,
  89         .u.dw.write = NULL,
  90         },
  91        {}
  92};
  93
  94static int pm_caps_read(struct pci_dev *dev, int offset, u16 *value,
  95                        void *data)
  96{
  97        int err;
  98        u16 real_value;
  99
 100        err = pci_read_config_word(dev, offset, &real_value);
 101        if (err)
 102                goto out;
 103
 104        *value = real_value & ~PCI_PM_CAP_PME_MASK;
 105
 106out:
 107        return err;
 108}
 109
 110/* PM_OK_BITS specifies the bits that the driver domain is allowed to change.
 111 * Can't allow driver domain to enable PMEs - they're shared */
 112#define PM_OK_BITS (PCI_PM_CTRL_PME_STATUS|PCI_PM_CTRL_DATA_SEL_MASK)
 113
 114static int pm_ctrl_write(struct pci_dev *dev, int offset, u16 new_value,
 115                         void *data)
 116{
 117        int err;
 118        u16 old_value;
 119        pci_power_t new_state;
 120
 121        err = pci_read_config_word(dev, offset, &old_value);
 122        if (err)
 123                goto out;
 124
 125        new_state = (pci_power_t)(new_value & PCI_PM_CTRL_STATE_MASK);
 126
 127        new_value &= PM_OK_BITS;
 128        if ((old_value & PM_OK_BITS) != new_value) {
 129                new_value = (old_value & ~PM_OK_BITS) | new_value;
 130                err = pci_write_config_word(dev, offset, new_value);
 131                if (err)
 132                        goto out;
 133        }
 134
 135        /* Let pci core handle the power management change */
 136        dev_dbg(&dev->dev, "set power state to %x\n", new_state);
 137        err = pci_set_power_state(dev, new_state);
 138        if (err) {
 139                err = PCIBIOS_SET_FAILED;
 140                goto out;
 141        }
 142
 143 out:
 144        return err;
 145}
 146
 147/* Ensure PMEs are disabled */
 148static void *pm_ctrl_init(struct pci_dev *dev, int offset)
 149{
 150        int err;
 151        u16 value;
 152
 153        err = pci_read_config_word(dev, offset, &value);
 154        if (err)
 155                goto out;
 156
 157        if (value & PCI_PM_CTRL_PME_ENABLE) {
 158                value &= ~PCI_PM_CTRL_PME_ENABLE;
 159                err = pci_write_config_word(dev, offset, value);
 160        }
 161
 162out:
 163        return ERR_PTR(err);
 164}
 165
 166static const struct config_field caplist_pm[] = {
 167        {
 168                .offset     = PCI_PM_PMC,
 169                .size       = 2,
 170                .u.w.read   = pm_caps_read,
 171        },
 172        {
 173                .offset     = PCI_PM_CTRL,
 174                .size       = 2,
 175                .init       = pm_ctrl_init,
 176                .u.w.read   = xen_pcibk_read_config_word,
 177                .u.w.write  = pm_ctrl_write,
 178        },
 179        {
 180                .offset     = PCI_PM_PPB_EXTENSIONS,
 181                .size       = 1,
 182                .u.b.read   = xen_pcibk_read_config_byte,
 183        },
 184        {
 185                .offset     = PCI_PM_DATA_REGISTER,
 186                .size       = 1,
 187                .u.b.read   = xen_pcibk_read_config_byte,
 188        },
 189        {}
 190};
 191
 192static struct msi_msix_field_config {
 193        u16          enable_bit; /* bit for enabling MSI/MSI-X */
 194        unsigned int int_type;   /* interrupt type for exclusiveness check */
 195} msi_field_config = {
 196        .enable_bit     = PCI_MSI_FLAGS_ENABLE,
 197        .int_type       = INTERRUPT_TYPE_MSI,
 198}, msix_field_config = {
 199        .enable_bit     = PCI_MSIX_FLAGS_ENABLE,
 200        .int_type       = INTERRUPT_TYPE_MSIX,
 201};
 202
 203static void *msi_field_init(struct pci_dev *dev, int offset)
 204{
 205        return &msi_field_config;
 206}
 207
 208static void *msix_field_init(struct pci_dev *dev, int offset)
 209{
 210        return &msix_field_config;
 211}
 212
 213static int msi_msix_flags_write(struct pci_dev *dev, int offset, u16 new_value,
 214                                void *data)
 215{
 216        int err;
 217        u16 old_value;
 218        const struct msi_msix_field_config *field_config = data;
 219        const struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev);
 220
 221        if (xen_pcibk_permissive || dev_data->permissive)
 222                goto write;
 223
 224        err = pci_read_config_word(dev, offset, &old_value);
 225        if (err)
 226                return err;
 227
 228        if (new_value == old_value)
 229                return 0;
 230
 231        if (!dev_data->allow_interrupt_control ||
 232            (new_value ^ old_value) & ~field_config->enable_bit)
 233                return PCIBIOS_SET_FAILED;
 234
 235        if (new_value & field_config->enable_bit) {
 236                /* don't allow enabling together with other interrupt types */
 237                int int_type = xen_pcibk_get_interrupt_type(dev);
 238
 239                if (int_type == INTERRUPT_TYPE_NONE ||
 240                    int_type == field_config->int_type)
 241                        goto write;
 242                return PCIBIOS_SET_FAILED;
 243        }
 244
 245write:
 246        return pci_write_config_word(dev, offset, new_value);
 247}
 248
 249static const struct config_field caplist_msix[] = {
 250        {
 251                .offset    = PCI_MSIX_FLAGS,
 252                .size      = 2,
 253                .init      = msix_field_init,
 254                .u.w.read  = xen_pcibk_read_config_word,
 255                .u.w.write = msi_msix_flags_write,
 256        },
 257        {}
 258};
 259
 260static const struct config_field caplist_msi[] = {
 261        {
 262                .offset    = PCI_MSI_FLAGS,
 263                .size      = 2,
 264                .init      = msi_field_init,
 265                .u.w.read  = xen_pcibk_read_config_word,
 266                .u.w.write = msi_msix_flags_write,
 267        },
 268        {}
 269};
 270
 271static struct xen_pcibk_config_capability xen_pcibk_config_capability_pm = {
 272        .capability = PCI_CAP_ID_PM,
 273        .fields = caplist_pm,
 274};
 275static struct xen_pcibk_config_capability xen_pcibk_config_capability_vpd = {
 276        .capability = PCI_CAP_ID_VPD,
 277        .fields = caplist_vpd,
 278};
 279static struct xen_pcibk_config_capability xen_pcibk_config_capability_msi = {
 280        .capability = PCI_CAP_ID_MSI,
 281        .fields = caplist_msi,
 282};
 283static struct xen_pcibk_config_capability xen_pcibk_config_capability_msix = {
 284        .capability = PCI_CAP_ID_MSIX,
 285        .fields = caplist_msix,
 286};
 287
 288int xen_pcibk_config_capability_init(void)
 289{
 290        register_capability(&xen_pcibk_config_capability_vpd);
 291        register_capability(&xen_pcibk_config_capability_pm);
 292        register_capability(&xen_pcibk_config_capability_msi);
 293        register_capability(&xen_pcibk_config_capability_msix);
 294
 295        return 0;
 296}
 297