• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 LIST_HEAD(xen_pcibk_quirks);
18 static inline const struct pci_device_id *
match_one_device(const struct pci_device_id * id,const struct pci_dev * dev)19 match_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 
xen_pcibk_find_quirk(struct pci_dev * dev)32 static 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");
42 out:
43 	return tmp_quirk;
44 }
45 
register_quirk(struct xen_pcibk_config_quirk * quirk)46 static inline void register_quirk(struct xen_pcibk_config_quirk *quirk)
47 {
48 	list_add_tail(&quirk->quirks_list, &xen_pcibk_quirks);
49 }
50 
xen_pcibk_field_is_dup(struct pci_dev * dev,unsigned int reg)51 int 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 
xen_pcibk_config_quirks_add_field(struct pci_dev * dev,struct config_field * field)66 int 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 
91 out:
92 	return err;
93 }
94 
xen_pcibk_config_quirks_init(struct pci_dev * dev)95 int 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);
117 out:
118 	return ret;
119 }
120 
xen_pcibk_config_field_free(struct config_field * field)121 void xen_pcibk_config_field_free(struct config_field *field)
122 {
123 	kfree(field);
124 }
125 
xen_pcibk_config_quirk_release(struct pci_dev * dev)126 int 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 
140 out:
141 	return ret;
142 }
143