/root/src/xen/xen/drivers/vpci/msi.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Handlers for accesses to the MSI capability structure. |
3 | | * |
4 | | * Copyright (C) 2017 Citrix Systems R&D |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or |
7 | | * modify it under the terms and conditions of the GNU General Public |
8 | | * License, version 2, as published by the Free Software Foundation. |
9 | | * |
10 | | * This program is distributed in the hope that it will be useful, |
11 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | | * General Public License for more details. |
14 | | * |
15 | | * You should have received a copy of the GNU General Public |
16 | | * License along with this program; If not, see <http://www.gnu.org/licenses/>. |
17 | | */ |
18 | | |
19 | | #include <xen/sched.h> |
20 | | #include <xen/softirq.h> |
21 | | #include <xen/vpci.h> |
22 | | |
23 | | #include <asm/msi.h> |
24 | | |
25 | | static uint32_t control_read(const struct pci_dev *pdev, unsigned int reg, |
26 | | void *data) |
27 | 27 | { |
28 | 27 | const struct vpci_msi *msi = data; |
29 | 27 | |
30 | 27 | return MASK_INSR(fls(msi->max_vectors) - 1, PCI_MSI_FLAGS_QMASK) | |
31 | 27 | MASK_INSR(fls(msi->vectors) - 1, PCI_MSI_FLAGS_QSIZE) | |
32 | 21 | (msi->enabled ? PCI_MSI_FLAGS_ENABLE : 0) | |
33 | 20 | (msi->masking ? PCI_MSI_FLAGS_MASKBIT : 0) | |
34 | 19 | (msi->address64 ? PCI_MSI_FLAGS_64BIT : 0); |
35 | 27 | } |
36 | | |
37 | | static void control_write(const struct pci_dev *pdev, unsigned int reg, |
38 | | uint32_t val, void *data) |
39 | 33 | { |
40 | 33 | struct vpci_msi *msi = data; |
41 | 33 | unsigned int vectors = min_t(uint8_t, |
42 | 33 | 1u << MASK_EXTR(val, PCI_MSI_FLAGS_QSIZE), |
43 | 33 | msi->max_vectors); |
44 | 33 | bool new_enabled = val & PCI_MSI_FLAGS_ENABLE; |
45 | 33 | |
46 | 33 | /* |
47 | 33 | * No change if the enable field and the number of vectors is |
48 | 33 | * the same or the device is not enabled, in which case the |
49 | 33 | * vectors field can be updated directly. |
50 | 33 | */ |
51 | 33 | if ( new_enabled == msi->enabled && |
52 | 27 | (vectors == msi->vectors || !msi->enabled) ) |
53 | 27 | { |
54 | 27 | msi->vectors = vectors; |
55 | 27 | return; |
56 | 27 | } |
57 | 33 | |
58 | 6 | if ( new_enabled ) |
59 | 6 | { |
60 | 6 | unsigned int i; |
61 | 6 | |
62 | 6 | /* |
63 | 6 | * If the device is already enabled it means the number of |
64 | 6 | * enabled messages has changed. Disable and re-enable the |
65 | 6 | * device in order to apply the change. |
66 | 6 | */ |
67 | 6 | if ( msi->enabled ) |
68 | 0 | { |
69 | 0 | vpci_msi_arch_disable(msi, pdev); |
70 | 0 | msi->enabled = false; |
71 | 0 | } |
72 | 6 | |
73 | 6 | if ( vpci_msi_arch_enable(msi, pdev, vectors) ) |
74 | 0 | return; |
75 | 6 | |
76 | 6 | for ( i = 0; msi->masking && i < vectors; i++ ) |
77 | 0 | vpci_msi_arch_mask(msi, pdev, i, (msi->mask >> i) & 1); |
78 | 6 | } |
79 | 6 | else |
80 | 0 | vpci_msi_arch_disable(msi, pdev); |
81 | 6 | |
82 | 6 | msi->vectors = vectors; |
83 | 6 | msi->enabled = new_enabled; |
84 | 6 | |
85 | 6 | pci_conf_write16(pdev->seg, pdev->bus, PCI_SLOT(pdev->devfn), |
86 | 6 | PCI_FUNC(pdev->devfn), reg, |
87 | 6 | control_read(pdev, reg, data)); |
88 | 6 | } |
89 | | |
90 | | static void update_msi(const struct pci_dev *pdev, struct vpci_msi *msi) |
91 | 17 | { |
92 | 17 | if ( !msi->enabled ) |
93 | 17 | return; |
94 | 17 | |
95 | 0 | vpci_msi_arch_disable(msi, pdev); |
96 | 0 | if ( vpci_msi_arch_enable(msi, pdev, msi->vectors) ) |
97 | 0 | msi->enabled = false; |
98 | 0 | } |
99 | | |
100 | | /* Handlers for the address field (32bit or low part of a 64bit address). */ |
101 | | static uint32_t address_read(const struct pci_dev *pdev, unsigned int reg, |
102 | | void *data) |
103 | 0 | { |
104 | 0 | const struct vpci_msi *msi = data; |
105 | 0 |
|
106 | 0 | return msi->address; |
107 | 0 | } |
108 | | |
109 | | static void address_write(const struct pci_dev *pdev, unsigned int reg, |
110 | | uint32_t val, void *data) |
111 | 6 | { |
112 | 6 | struct vpci_msi *msi = data; |
113 | 6 | |
114 | 6 | /* Clear low part. */ |
115 | 6 | msi->address &= ~0xffffffffull; |
116 | 6 | msi->address |= val; |
117 | 6 | |
118 | 6 | update_msi(pdev, msi); |
119 | 6 | } |
120 | | |
121 | | /* Handlers for the high part of a 64bit address field. */ |
122 | | static uint32_t address_hi_read(const struct pci_dev *pdev, unsigned int reg, |
123 | | void *data) |
124 | 0 | { |
125 | 0 | const struct vpci_msi *msi = data; |
126 | 0 |
|
127 | 0 | return msi->address >> 32; |
128 | 0 | } |
129 | | |
130 | | static void address_hi_write(const struct pci_dev *pdev, unsigned int reg, |
131 | | uint32_t val, void *data) |
132 | 5 | { |
133 | 5 | struct vpci_msi *msi = data; |
134 | 5 | |
135 | 5 | /* Clear and update high part. */ |
136 | 5 | msi->address &= 0xffffffff; |
137 | 5 | msi->address |= (uint64_t)val << 32; |
138 | 5 | |
139 | 5 | update_msi(pdev, msi); |
140 | 5 | } |
141 | | |
142 | | /* Handlers for the data field. */ |
143 | | static uint32_t data_read(const struct pci_dev *pdev, unsigned int reg, |
144 | | void *data) |
145 | 0 | { |
146 | 0 | const struct vpci_msi *msi = data; |
147 | 0 |
|
148 | 0 | return msi->data; |
149 | 0 | } |
150 | | |
151 | | static void data_write(const struct pci_dev *pdev, unsigned int reg, |
152 | | uint32_t val, void *data) |
153 | 6 | { |
154 | 6 | struct vpci_msi *msi = data; |
155 | 6 | |
156 | 6 | msi->data = val; |
157 | 6 | |
158 | 6 | update_msi(pdev, msi); |
159 | 6 | } |
160 | | |
161 | | /* Handlers for the MSI mask bits. */ |
162 | | static uint32_t mask_read(const struct pci_dev *pdev, unsigned int reg, |
163 | | void *data) |
164 | 0 | { |
165 | 0 | const struct vpci_msi *msi = data; |
166 | 0 |
|
167 | 0 | return msi->mask; |
168 | 0 | } |
169 | | |
170 | | static void mask_write(const struct pci_dev *pdev, unsigned int reg, |
171 | | uint32_t val, void *data) |
172 | 0 | { |
173 | 0 | struct vpci_msi *msi = data; |
174 | 0 | uint32_t dmask = msi->mask ^ val; |
175 | 0 |
|
176 | 0 | if ( !dmask ) |
177 | 0 | return; |
178 | 0 |
|
179 | 0 | if ( msi->enabled ) |
180 | 0 | { |
181 | 0 | unsigned int i; |
182 | 0 |
|
183 | 0 | for ( i = ffs(dmask) - 1; dmask && i < msi->vectors; |
184 | 0 | i = ffs(dmask) - 1 ) |
185 | 0 | { |
186 | 0 | vpci_msi_arch_mask(msi, pdev, i, (val >> i) & 1); |
187 | 0 | __clear_bit(i, &dmask); |
188 | 0 | } |
189 | 0 | } |
190 | 0 |
|
191 | 0 | msi->mask = val; |
192 | 0 | } |
193 | | |
194 | | static int init_msi(struct pci_dev *pdev) |
195 | 68 | { |
196 | 68 | uint8_t seg = pdev->seg, bus = pdev->bus; |
197 | 68 | uint8_t slot = PCI_SLOT(pdev->devfn), func = PCI_FUNC(pdev->devfn); |
198 | 68 | struct vpci_msi *msi; |
199 | 68 | unsigned int pos = pci_find_cap_offset(seg, bus, slot, func, |
200 | 68 | PCI_CAP_ID_MSI); |
201 | 68 | uint16_t control; |
202 | 68 | int ret; |
203 | 68 | |
204 | 68 | if ( !pos ) |
205 | 47 | return 0; |
206 | 68 | |
207 | 21 | msi = xzalloc(struct vpci_msi); |
208 | 21 | if ( !msi ) |
209 | 0 | return -ENOMEM; |
210 | 21 | |
211 | 21 | ret = vpci_add_register(pdev->vpci, control_read, control_write, |
212 | 21 | msi_control_reg(pos), 2, msi); |
213 | 21 | if ( ret ) |
214 | 0 | { |
215 | 0 | xfree(msi); |
216 | 0 | return ret; |
217 | 0 | } |
218 | 21 | |
219 | 21 | /* Get the maximum number of vectors the device supports. */ |
220 | 21 | control = pci_conf_read16(seg, bus, slot, func, msi_control_reg(pos)); |
221 | 21 | msi->max_vectors = multi_msi_capable(control); |
222 | 21 | ASSERT(msi->max_vectors <= 32); |
223 | 21 | |
224 | 21 | /* The multiple message enable is 0 after reset (1 message enabled). */ |
225 | 21 | msi->vectors = 1; |
226 | 21 | |
227 | 21 | /* No PIRQ bound yet. */ |
228 | 21 | vpci_msi_arch_init(msi); |
229 | 21 | |
230 | 21 | msi->address64 = is_64bit_address(control); |
231 | 21 | msi->masking = is_mask_bit_support(control); |
232 | 21 | |
233 | 21 | ret = vpci_add_register(pdev->vpci, address_read, address_write, |
234 | 21 | msi_lower_address_reg(pos), 4, msi); |
235 | 21 | if ( ret ) |
236 | 0 | { |
237 | 0 | xfree(msi); |
238 | 0 | return ret; |
239 | 0 | } |
240 | 21 | |
241 | 21 | ret = vpci_add_register(pdev->vpci, data_read, data_write, |
242 | 21 | msi_data_reg(pos, msi->address64), 2, |
243 | 21 | msi); |
244 | 21 | if ( ret ) |
245 | 0 | { |
246 | 0 | xfree(msi); |
247 | 0 | return ret; |
248 | 0 | } |
249 | 21 | |
250 | 21 | if ( msi->address64 ) |
251 | 14 | { |
252 | 14 | ret = vpci_add_register(pdev->vpci, address_hi_read, address_hi_write, |
253 | 14 | msi_upper_address_reg(pos), 4, msi); |
254 | 14 | if ( ret ) |
255 | 0 | { |
256 | 0 | xfree(msi); |
257 | 0 | return ret; |
258 | 0 | } |
259 | 14 | } |
260 | 21 | |
261 | 21 | if ( msi->masking ) |
262 | 7 | { |
263 | 7 | ret = vpci_add_register(pdev->vpci, mask_read, mask_write, |
264 | 7 | msi_mask_bits_reg(pos, msi->address64), 4, |
265 | 7 | msi); |
266 | 7 | if ( ret ) |
267 | 0 | { |
268 | 0 | xfree(msi); |
269 | 0 | return ret; |
270 | 0 | } |
271 | 7 | |
272 | 7 | ret = vpci_add_register(pdev->vpci, vpci_hw_read32, NULL, |
273 | 7 | msi_pending_bits_reg(pos, msi->address64), 4, |
274 | 7 | msi); |
275 | 7 | if ( ret ) |
276 | 0 | { |
277 | 0 | xfree(msi); |
278 | 0 | return ret; |
279 | 0 | } |
280 | 7 | } |
281 | 21 | |
282 | 21 | pdev->vpci->msi = msi; |
283 | 21 | |
284 | 21 | return 0; |
285 | 21 | } |
286 | | REGISTER_VPCI_INIT(init_msi, VPCI_PRIORITY_LOW); |
287 | | |
288 | | void vpci_dump_msi(void) |
289 | 0 | { |
290 | 0 | const struct domain *d; |
291 | 0 |
|
292 | 0 | rcu_read_lock(&domlist_read_lock); |
293 | 0 | for_each_domain ( d ) |
294 | 0 | { |
295 | 0 | const struct pci_dev *pdev; |
296 | 0 |
|
297 | 0 | if ( !has_vpci(d) ) |
298 | 0 | continue; |
299 | 0 |
|
300 | 0 | printk("vPCI MSI/MSI-X d%d\n", d->domain_id); |
301 | 0 |
|
302 | 0 | list_for_each_entry ( pdev, &d->arch.pdev_list, domain_list ) |
303 | 0 | { |
304 | 0 | uint8_t seg = pdev->seg, bus = pdev->bus; |
305 | 0 | uint8_t slot = PCI_SLOT(pdev->devfn), func = PCI_FUNC(pdev->devfn); |
306 | 0 | const struct vpci_msi *msi = pdev->vpci->msi; |
307 | 0 | const struct vpci_msix *msix = pdev->vpci->msix; |
308 | 0 |
|
309 | 0 | if ( msi || msix ) |
310 | 0 | printk("%04x:%02x:%02x.%u\n", seg, bus, slot, func); |
311 | 0 |
|
312 | 0 | if ( !spin_trylock(&pdev->vpci->lock) ) |
313 | 0 | { |
314 | 0 | printk("Unable to get vPCI lock, skipping\n"); |
315 | 0 | continue; |
316 | 0 | } |
317 | 0 |
|
318 | 0 | if ( msi ) |
319 | 0 | { |
320 | 0 | printk(" MSI\n"); |
321 | 0 |
|
322 | 0 | printk(" enabled: %d 64-bit: %d", |
323 | 0 | msi->enabled, msi->address64); |
324 | 0 | if ( msi->masking ) |
325 | 0 | printk(" mask=%08x", msi->mask); |
326 | 0 | printk(" vectors max: %u enabled: %u\n", |
327 | 0 | msi->max_vectors, msi->vectors); |
328 | 0 |
|
329 | 0 | vpci_msi_arch_print(msi); |
330 | 0 | } |
331 | 0 |
|
332 | 0 | if ( msix ) |
333 | 0 | { |
334 | 0 | unsigned int i; |
335 | 0 |
|
336 | 0 | printk(" MSI-X\n"); |
337 | 0 |
|
338 | 0 | printk(" entries: %u maskall: %d enabled: %d\n", |
339 | 0 | msix->max_entries, msix->masked, msix->enabled); |
340 | 0 |
|
341 | 0 | for ( i = 0; i < msix->max_entries; i++ ) |
342 | 0 | { |
343 | 0 | printk(" %4u ", i); |
344 | 0 | vpci_msix_arch_print_entry(&msix->entries[i]); |
345 | 0 | } |
346 | 0 | } |
347 | 0 |
|
348 | 0 | spin_unlock(&pdev->vpci->lock); |
349 | 0 | process_pending_softirqs(); |
350 | 0 | } |
351 | 0 | } |
352 | 0 | rcu_read_unlock(&domlist_read_lock); |
353 | 0 | } |
354 | | |
355 | | /* |
356 | | * Local variables: |
357 | | * mode: C |
358 | | * c-file-style: "BSD" |
359 | | * c-basic-offset: 4 |
360 | | * tab-width: 4 |
361 | | * indent-tabs-mode: nil |
362 | | * End: |
363 | | */ |