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 * |
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 | |
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(id: &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 | |
46 | static inline void register_quirk(struct xen_pcibk_config_quirk *quirk) |
47 | { |
48 | list_add_tail(new: &quirk->quirks_list, head: &xen_pcibk_quirks); |
49 | } |
50 | |
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(pdev: 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 | |
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 | |
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(size: 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 | |
121 | void xen_pcibk_config_field_free(struct config_field *field) |
122 | { |
123 | kfree(objp: field); |
124 | } |
125 | |
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(entry: &quirk->quirks_list); |
138 | kfree(objp: quirk); |
139 | |
140 | out: |
141 | return ret; |
142 | } |
143 | |