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 | |
14 | static LIST_HEAD(capabilities); |
15 | struct 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 | |
24 | static const struct config_field [] = { |
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 | |
34 | static inline void register_capability(struct xen_pcibk_config_capability *cap) |
35 | { |
36 | list_add_tail(new: &cap->cap_list, head: &capabilities); |
37 | } |
38 | |
39 | int 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: 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 | field: caplist_header, |
53 | offset: cap_offset); |
54 | if (err) |
55 | goto out; |
56 | err = xen_pcibk_config_add_fields_offset(dev, |
57 | field: cap->fields, |
58 | offset: cap_offset); |
59 | if (err) |
60 | goto out; |
61 | } |
62 | } |
63 | |
64 | out: |
65 | return err; |
66 | } |
67 | |
68 | static 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, where: offset, val: value); |
76 | } |
77 | |
78 | static 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 | |
94 | static 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, where: offset, val: &real_value); |
101 | if (err) |
102 | goto out; |
103 | |
104 | *value = real_value & ~PCI_PM_CAP_PME_MASK; |
105 | |
106 | out: |
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 | |
114 | static 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, where: offset, val: &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, where: offset, val: 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, state: 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 */ |
148 | static 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, where: offset, val: &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, where: offset, val: value); |
160 | } |
161 | |
162 | out: |
163 | return err ? ERR_PTR(error: err) : NULL; |
164 | } |
165 | |
166 | static 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 | |
192 | static struct msi_msix_field_config { |
193 | u16 enable_bit; /* bit for enabling MSI/MSI-X */ |
194 | u16 allowed_bits; /* bits allowed to be changed */ |
195 | unsigned int int_type; /* interrupt type for exclusiveness check */ |
196 | } msi_field_config = { |
197 | .enable_bit = PCI_MSI_FLAGS_ENABLE, |
198 | .allowed_bits = PCI_MSI_FLAGS_ENABLE, |
199 | .int_type = INTERRUPT_TYPE_MSI, |
200 | }, msix_field_config = { |
201 | .enable_bit = PCI_MSIX_FLAGS_ENABLE, |
202 | .allowed_bits = PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL, |
203 | .int_type = INTERRUPT_TYPE_MSIX, |
204 | }; |
205 | |
206 | static void *msi_field_init(struct pci_dev *dev, int offset) |
207 | { |
208 | return &msi_field_config; |
209 | } |
210 | |
211 | static void *msix_field_init(struct pci_dev *dev, int offset) |
212 | { |
213 | return &msix_field_config; |
214 | } |
215 | |
216 | static int msi_msix_flags_write(struct pci_dev *dev, int offset, u16 new_value, |
217 | void *data) |
218 | { |
219 | int err; |
220 | u16 old_value; |
221 | const struct msi_msix_field_config *field_config = data; |
222 | const struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(pdev: dev); |
223 | |
224 | if (xen_pcibk_permissive || dev_data->permissive) |
225 | goto write; |
226 | |
227 | err = pci_read_config_word(dev, where: offset, val: &old_value); |
228 | if (err) |
229 | return err; |
230 | |
231 | if (new_value == old_value) |
232 | return 0; |
233 | |
234 | if (!dev_data->allow_interrupt_control || |
235 | (new_value ^ old_value) & ~field_config->allowed_bits) |
236 | return PCIBIOS_SET_FAILED; |
237 | |
238 | if (new_value & field_config->enable_bit) { |
239 | /* |
240 | * Don't allow enabling together with other interrupt type, but do |
241 | * allow enabling MSI(-X) while INTx is still active to please Linuxes |
242 | * MSI(-X) startup sequence. It is safe to do, as according to PCI |
243 | * spec, device with enabled MSI(-X) shouldn't use INTx. |
244 | */ |
245 | int int_type = xen_pcibk_get_interrupt_type(dev); |
246 | |
247 | if (int_type == INTERRUPT_TYPE_NONE || |
248 | int_type == INTERRUPT_TYPE_INTX || |
249 | int_type == field_config->int_type) |
250 | goto write; |
251 | return PCIBIOS_SET_FAILED; |
252 | } |
253 | |
254 | write: |
255 | return pci_write_config_word(dev, where: offset, val: new_value); |
256 | } |
257 | |
258 | static const struct config_field caplist_msix[] = { |
259 | { |
260 | .offset = PCI_MSIX_FLAGS, |
261 | .size = 2, |
262 | .init = msix_field_init, |
263 | .u.w.read = xen_pcibk_read_config_word, |
264 | .u.w.write = msi_msix_flags_write, |
265 | }, |
266 | {} |
267 | }; |
268 | |
269 | static const struct config_field caplist_msi[] = { |
270 | { |
271 | .offset = PCI_MSI_FLAGS, |
272 | .size = 2, |
273 | .init = msi_field_init, |
274 | .u.w.read = xen_pcibk_read_config_word, |
275 | .u.w.write = msi_msix_flags_write, |
276 | }, |
277 | {} |
278 | }; |
279 | |
280 | static struct xen_pcibk_config_capability xen_pcibk_config_capability_pm = { |
281 | .capability = PCI_CAP_ID_PM, |
282 | .fields = caplist_pm, |
283 | }; |
284 | static struct xen_pcibk_config_capability xen_pcibk_config_capability_vpd = { |
285 | .capability = PCI_CAP_ID_VPD, |
286 | .fields = caplist_vpd, |
287 | }; |
288 | static struct xen_pcibk_config_capability xen_pcibk_config_capability_msi = { |
289 | .capability = PCI_CAP_ID_MSI, |
290 | .fields = caplist_msi, |
291 | }; |
292 | static struct xen_pcibk_config_capability xen_pcibk_config_capability_msix = { |
293 | .capability = PCI_CAP_ID_MSIX, |
294 | .fields = caplist_msix, |
295 | }; |
296 | |
297 | int xen_pcibk_config_capability_init(void) |
298 | { |
299 | register_capability(cap: &xen_pcibk_config_capability_vpd); |
300 | register_capability(cap: &xen_pcibk_config_capability_pm); |
301 | register_capability(cap: &xen_pcibk_config_capability_msi); |
302 | register_capability(cap: &xen_pcibk_config_capability_msix); |
303 | |
304 | return 0; |
305 | } |
306 | |