conf_space_quirks.c (3210B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * PCI Backend - Handle special overlays for broken devices. 4 * 5 * Author: Ryan Wilson <hap9@epoch.ncsc.mil> 6 * Author: Chris Bookholt <hap10@epoch.ncsc.mil> 7 */ 8 9#define dev_fmt(fmt) DRV_NAME ": " fmt 10 11#include <linux/kernel.h> 12#include <linux/pci.h> 13#include "pciback.h" 14#include "conf_space.h" 15#include "conf_space_quirks.h" 16 17LIST_HEAD(xen_pcibk_quirks); 18static inline const struct pci_device_id * 19match_one_device(const struct pci_device_id *id, const struct pci_dev *dev) 20{ 21 if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) && 22 (id->device == PCI_ANY_ID || id->device == dev->device) && 23 (id->subvendor == PCI_ANY_ID || 24 id->subvendor == dev->subsystem_vendor) && 25 (id->subdevice == PCI_ANY_ID || 26 id->subdevice == dev->subsystem_device) && 27 !((id->class ^ dev->class) & id->class_mask)) 28 return id; 29 return NULL; 30} 31 32static struct xen_pcibk_config_quirk *xen_pcibk_find_quirk(struct pci_dev *dev) 33{ 34 struct xen_pcibk_config_quirk *tmp_quirk; 35 36 list_for_each_entry(tmp_quirk, &xen_pcibk_quirks, quirks_list) 37 if (match_one_device(&tmp_quirk->devid, dev) != NULL) 38 goto out; 39 tmp_quirk = NULL; 40 dev_printk(KERN_DEBUG, &dev->dev, 41 "quirk didn't match any device known\n"); 42out: 43 return tmp_quirk; 44} 45 46static inline void register_quirk(struct xen_pcibk_config_quirk *quirk) 47{ 48 list_add_tail(&quirk->quirks_list, &xen_pcibk_quirks); 49} 50 51int xen_pcibk_field_is_dup(struct pci_dev *dev, unsigned int reg) 52{ 53 int ret = 0; 54 struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev); 55 struct config_field_entry *cfg_entry; 56 57 list_for_each_entry(cfg_entry, &dev_data->config_fields, list) { 58 if (OFFSET(cfg_entry) == reg) { 59 ret = 1; 60 break; 61 } 62 } 63 return ret; 64} 65 66int xen_pcibk_config_quirks_add_field(struct pci_dev *dev, struct config_field 67 *field) 68{ 69 int err = 0; 70 71 switch (field->size) { 72 case 1: 73 field->u.b.read = xen_pcibk_read_config_byte; 74 field->u.b.write = xen_pcibk_write_config_byte; 75 break; 76 case 2: 77 field->u.w.read = xen_pcibk_read_config_word; 78 field->u.w.write = xen_pcibk_write_config_word; 79 break; 80 case 4: 81 field->u.dw.read = xen_pcibk_read_config_dword; 82 field->u.dw.write = xen_pcibk_write_config_dword; 83 break; 84 default: 85 err = -EINVAL; 86 goto out; 87 } 88 89 xen_pcibk_config_add_field(dev, field); 90 91out: 92 return err; 93} 94 95int xen_pcibk_config_quirks_init(struct pci_dev *dev) 96{ 97 struct xen_pcibk_config_quirk *quirk; 98 int ret = 0; 99 100 quirk = kzalloc(sizeof(*quirk), GFP_KERNEL); 101 if (!quirk) { 102 ret = -ENOMEM; 103 goto out; 104 } 105 106 quirk->devid.vendor = dev->vendor; 107 quirk->devid.device = dev->device; 108 quirk->devid.subvendor = dev->subsystem_vendor; 109 quirk->devid.subdevice = dev->subsystem_device; 110 quirk->devid.class = 0; 111 quirk->devid.class_mask = 0; 112 quirk->devid.driver_data = 0UL; 113 114 quirk->pdev = dev; 115 116 register_quirk(quirk); 117out: 118 return ret; 119} 120 121void xen_pcibk_config_field_free(struct config_field *field) 122{ 123 kfree(field); 124} 125 126int xen_pcibk_config_quirk_release(struct pci_dev *dev) 127{ 128 struct xen_pcibk_config_quirk *quirk; 129 int ret = 0; 130 131 quirk = xen_pcibk_find_quirk(dev); 132 if (!quirk) { 133 ret = -ENXIO; 134 goto out; 135 } 136 137 list_del(&quirk->quirks_list); 138 kfree(quirk); 139 140out: 141 return ret; 142}