/root/src/xen/xen/drivers/passthrough/vtd/intremap.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2006, Intel Corporation. |
3 | | * |
4 | | * This program is free software; you can redistribute it and/or modify it |
5 | | * under the terms and conditions of the GNU General Public License, |
6 | | * version 2, as published by the Free Software Foundation. |
7 | | * |
8 | | * This program is distributed in the hope it will be useful, but WITHOUT |
9 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
10 | | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
11 | | * more details. |
12 | | * |
13 | | * You should have received a copy of the GNU General Public License along with |
14 | | * this program; If not, see <http://www.gnu.org/licenses/>. |
15 | | * |
16 | | * Copyright (C) Allen Kay <allen.m.kay@intel.com> |
17 | | * Copyright (C) Xiaohui Xin <xiaohui.xin@intel.com> |
18 | | */ |
19 | | |
20 | | #include <xen/irq.h> |
21 | | #include <xen/sched.h> |
22 | | #include <xen/iommu.h> |
23 | | #include <xen/time.h> |
24 | | #include <xen/list.h> |
25 | | #include <xen/pci.h> |
26 | | #include <xen/pci_regs.h> |
27 | | #include "iommu.h" |
28 | | #include "dmar.h" |
29 | | #include "vtd.h" |
30 | | #include "extern.h" |
31 | | |
32 | | #include <asm/apic.h> |
33 | | #include <asm/io_apic.h> |
34 | 5 | #define nr_ioapic_entries(i) nr_ioapic_entries[i] |
35 | | |
36 | | /* |
37 | | * source validation type (SVT) |
38 | | */ |
39 | | #define SVT_NO_VERIFY 0x0 /* no verification is required */ |
40 | 135 | #define SVT_VERIFY_SID_SQ 0x1 /* verify using SID and SQ fiels */ |
41 | 0 | #define SVT_VERIFY_BUS 0x2 /* verify bus of request-id */ |
42 | | |
43 | | /* |
44 | | * source-id qualifier (SQ) |
45 | | */ |
46 | 135 | #define SQ_ALL_16 0x0 /* verify all 16 bits of request-id */ |
47 | 0 | #define SQ_13_IGNORE_1 0x1 /* verify most significant 13 bits, ignore |
48 | | * the third least significant bit |
49 | | */ |
50 | 0 | #define SQ_13_IGNORE_2 0x2 /* verify most significant 13 bits, ignore |
51 | | * the second and third least significant bits |
52 | | */ |
53 | 0 | #define SQ_13_IGNORE_3 0x3 /* verify most significant 13 bits, ignore |
54 | | * the least three significant bits |
55 | | */ |
56 | | |
57 | | /* apic_pin_2_ir_idx[apicid][pin] = interrupt remapping table index */ |
58 | | static int **apic_pin_2_ir_idx; |
59 | | |
60 | | static int init_apic_pin_2_ir_idx(void) |
61 | 1 | { |
62 | 1 | int *_apic_pin_2_ir_idx; |
63 | 1 | unsigned int nr_pins, i; |
64 | 1 | |
65 | 1 | /* Here we shouldn't need to re-init when resuming from S3. */ |
66 | 1 | if ( apic_pin_2_ir_idx != NULL ) |
67 | 0 | return 0; |
68 | 1 | |
69 | 1 | nr_pins = 0; |
70 | 3 | for ( i = 0; i < nr_ioapics; i++ ) |
71 | 2 | nr_pins += nr_ioapic_entries(i); |
72 | 1 | |
73 | 1 | _apic_pin_2_ir_idx = xmalloc_array(int, nr_pins); |
74 | 1 | apic_pin_2_ir_idx = xmalloc_array(int *, nr_ioapics); |
75 | 1 | if ( (_apic_pin_2_ir_idx == NULL) || (apic_pin_2_ir_idx == NULL) ) |
76 | 0 | { |
77 | 0 | xfree(_apic_pin_2_ir_idx); |
78 | 0 | xfree(apic_pin_2_ir_idx); |
79 | 0 | return -ENOMEM; |
80 | 0 | } |
81 | 1 | |
82 | 49 | for ( i = 0; i < nr_pins; i++ ) |
83 | 48 | _apic_pin_2_ir_idx[i] = -1; |
84 | 1 | |
85 | 1 | nr_pins = 0; |
86 | 3 | for ( i = 0; i < nr_ioapics; i++ ) |
87 | 2 | { |
88 | 2 | apic_pin_2_ir_idx[i] = &_apic_pin_2_ir_idx[nr_pins]; |
89 | 2 | nr_pins += nr_ioapic_entries(i); |
90 | 2 | } |
91 | 1 | |
92 | 1 | return 0; |
93 | 1 | } |
94 | | |
95 | | static u16 apicid_to_bdf(int apic_id) |
96 | 51 | { |
97 | 51 | struct acpi_drhd_unit *drhd = ioapic_to_drhd(apic_id); |
98 | 51 | struct acpi_ioapic_unit *acpi_ioapic_unit; |
99 | 51 | |
100 | 51 | list_for_each_entry ( acpi_ioapic_unit, &drhd->ioapic_list, list ) |
101 | 102 | if ( acpi_ioapic_unit->apic_id == apic_id ) |
102 | 51 | return acpi_ioapic_unit->ioapic.info; |
103 | 51 | |
104 | 0 | dprintk(XENLOG_ERR VTDPREFIX, "Didn't find the bdf for the apic_id!\n"); |
105 | 0 | return 0; |
106 | 51 | } |
107 | | |
108 | | static u16 hpetid_to_bdf(unsigned int hpet_id) |
109 | 0 | { |
110 | 0 | struct acpi_drhd_unit *drhd = hpet_to_drhd(hpet_id); |
111 | 0 | struct acpi_hpet_unit *acpi_hpet_unit; |
112 | 0 |
|
113 | 0 | list_for_each_entry ( acpi_hpet_unit, &drhd->hpet_list, list ) |
114 | 0 | if ( acpi_hpet_unit->id == hpet_id ) |
115 | 0 | return acpi_hpet_unit->bdf; |
116 | 0 |
|
117 | 0 | dprintk(XENLOG_ERR VTDPREFIX, "Didn't find the bdf for HPET %u!\n", hpet_id); |
118 | 0 | return 0; |
119 | 0 | } |
120 | | |
121 | | static void set_ire_sid(struct iremap_entry *ire, |
122 | | unsigned int svt, unsigned int sq, unsigned int sid) |
123 | 135 | { |
124 | 135 | ire->remap.svt = svt; |
125 | 135 | ire->remap.sq = sq; |
126 | 135 | ire->remap.sid = sid; |
127 | 135 | } |
128 | | |
129 | | static void set_ioapic_source_id(int apic_id, struct iremap_entry *ire) |
130 | 51 | { |
131 | 51 | set_ire_sid(ire, SVT_VERIFY_SID_SQ, SQ_ALL_16, |
132 | 51 | apicid_to_bdf(apic_id)); |
133 | 51 | } |
134 | | |
135 | | static void set_hpet_source_id(unsigned int id, struct iremap_entry *ire) |
136 | 0 | { |
137 | 0 | /* |
138 | 0 | * Should really use SQ_ALL_16. Some platforms are broken. |
139 | 0 | * While we figure out the right quirks for these broken platforms, use |
140 | 0 | * SQ_13_IGNORE_3 for now. |
141 | 0 | */ |
142 | 0 | set_ire_sid(ire, SVT_VERIFY_SID_SQ, SQ_13_IGNORE_3, hpetid_to_bdf(id)); |
143 | 0 | } |
144 | | |
145 | | bool_t __init iommu_supports_eim(void) |
146 | 2 | { |
147 | 2 | struct acpi_drhd_unit *drhd; |
148 | 2 | unsigned int apic; |
149 | 2 | |
150 | 2 | if ( !iommu_qinval || !iommu_intremap || list_empty(&acpi_drhd_units) ) |
151 | 0 | return 0; |
152 | 2 | |
153 | 2 | /* We MUST have a DRHD unit for each IOAPIC. */ |
154 | 6 | for ( apic = 0; apic < nr_ioapics; apic++ ) |
155 | 4 | if ( !ioapic_to_drhd(IO_APIC_ID(apic)) ) |
156 | 0 | { |
157 | 0 | dprintk(XENLOG_WARNING VTDPREFIX, |
158 | 0 | "There is not a DRHD for IOAPIC %#x (id: %#x)!\n", |
159 | 0 | apic, IO_APIC_ID(apic)); |
160 | 0 | return 0; |
161 | 0 | } |
162 | 2 | |
163 | 2 | for_each_drhd_unit ( drhd ) |
164 | 2 | if ( !ecap_queued_inval(drhd->iommu->ecap) || |
165 | 2 | !ecap_intr_remap(drhd->iommu->ecap) || |
166 | 2 | !ecap_eim(drhd->iommu->ecap) ) |
167 | 0 | return 0; |
168 | 2 | |
169 | 2 | return 1; |
170 | 2 | } |
171 | | |
172 | | /* |
173 | | * Assume iremap_lock has been acquired. It is to make sure software will not |
174 | | * change the same IRTE behind us. With this assumption, if only high qword or |
175 | | * low qword in IRTE is to be updated, this function's atomic variant can |
176 | | * present an atomic update to VT-d hardware even when cmpxchg16b |
177 | | * instruction is not supported. |
178 | | */ |
179 | | static void update_irte(struct iommu *iommu, struct iremap_entry *entry, |
180 | | const struct iremap_entry *new_ire, bool atomic) |
181 | 177 | { |
182 | 177 | ASSERT(spin_is_locked(&iommu_ir_ctrl(iommu)->iremap_lock)); |
183 | 177 | |
184 | 177 | if ( cpu_has_cx16 ) |
185 | 177 | { |
186 | 177 | __uint128_t ret; |
187 | 177 | struct iremap_entry old_ire; |
188 | 177 | |
189 | 177 | old_ire = *entry; |
190 | 177 | ret = cmpxchg16b(entry, &old_ire, new_ire); |
191 | 177 | |
192 | 177 | /* |
193 | 177 | * In the above, we use cmpxchg16 to atomically update the 128-bit |
194 | 177 | * IRTE, and the hardware cannot update the IRTE behind us, so |
195 | 177 | * the return value of cmpxchg16 should be the same as old_ire. |
196 | 177 | * This ASSERT validate it. |
197 | 177 | */ |
198 | 177 | ASSERT(ret == old_ire.val); |
199 | 177 | } |
200 | 177 | else |
201 | 0 | { |
202 | 0 | /* |
203 | 0 | * VT-d hardware doesn't update IRTEs behind us, nor the software |
204 | 0 | * since we hold iremap_lock. If the caller wants VT-d hardware to |
205 | 0 | * always see a consistent entry, but we can't meet it, a bug will |
206 | 0 | * be raised. |
207 | 0 | */ |
208 | 0 | if ( entry->lo == new_ire->lo ) |
209 | 0 | write_atomic(&entry->hi, new_ire->hi); |
210 | 0 | else if ( entry->hi == new_ire->hi ) |
211 | 0 | write_atomic(&entry->lo, new_ire->lo); |
212 | 0 | else if ( !atomic ) |
213 | 0 | *entry = *new_ire; |
214 | 0 | else |
215 | 0 | BUG(); |
216 | 0 | } |
217 | 177 | } |
218 | | |
219 | | /* Mark specified intr remap entry as free */ |
220 | | static void free_remap_entry(struct iommu *iommu, int index) |
221 | 0 | { |
222 | 0 | struct iremap_entry *iremap_entry = NULL, *iremap_entries, new_ire = { }; |
223 | 0 | struct ir_ctrl *ir_ctrl = iommu_ir_ctrl(iommu); |
224 | 0 |
|
225 | 0 | if ( index < 0 || index > IREMAP_ENTRY_NR - 1 ) |
226 | 0 | return; |
227 | 0 |
|
228 | 0 | ASSERT( spin_is_locked(&ir_ctrl->iremap_lock) ); |
229 | 0 |
|
230 | 0 | GET_IREMAP_ENTRY(ir_ctrl->iremap_maddr, index, |
231 | 0 | iremap_entries, iremap_entry); |
232 | 0 |
|
233 | 0 | update_irte(iommu, iremap_entry, &new_ire, false); |
234 | 0 | iommu_flush_cache_entry(iremap_entry, sizeof(*iremap_entry)); |
235 | 0 | iommu_flush_iec_index(iommu, 0, index); |
236 | 0 |
|
237 | 0 | unmap_vtd_domain_page(iremap_entries); |
238 | 0 | ir_ctrl->iremap_num--; |
239 | 0 | } |
240 | | |
241 | | /* |
242 | | * Look for a free intr remap entry (or a contiguous set thereof). |
243 | | * Need hold iremap_lock, and setup returned entry before releasing lock. |
244 | | */ |
245 | | static unsigned int alloc_remap_entry(struct iommu *iommu, unsigned int nr) |
246 | 60 | { |
247 | 60 | struct iremap_entry *iremap_entries = NULL; |
248 | 60 | struct ir_ctrl *ir_ctrl = iommu_ir_ctrl(iommu); |
249 | 60 | unsigned int i, found; |
250 | 60 | |
251 | 60 | ASSERT( spin_is_locked(&ir_ctrl->iremap_lock) ); |
252 | 60 | |
253 | 1.83k | for ( found = i = 0; i < IREMAP_ENTRY_NR; i++ ) |
254 | 1.83k | { |
255 | 1.83k | struct iremap_entry *p; |
256 | 1.83k | if ( i % (1 << IREMAP_ENTRY_ORDER) == 0 ) |
257 | 60 | { |
258 | 60 | /* This entry across page boundry */ |
259 | 60 | if ( iremap_entries ) |
260 | 0 | unmap_vtd_domain_page(iremap_entries); |
261 | 60 | |
262 | 60 | GET_IREMAP_ENTRY(ir_ctrl->iremap_maddr, i, |
263 | 60 | iremap_entries, p); |
264 | 60 | } |
265 | 1.83k | else |
266 | 1.77k | p = &iremap_entries[i % (1 << IREMAP_ENTRY_ORDER)]; |
267 | 1.83k | |
268 | 1.83k | if ( p->val ) /* not a free entry */ |
269 | 1.77k | found = 0; |
270 | 60 | else if ( ++found == nr ) |
271 | 60 | break; |
272 | 1.83k | } |
273 | 60 | |
274 | 60 | if ( iremap_entries ) |
275 | 60 | unmap_vtd_domain_page(iremap_entries); |
276 | 60 | |
277 | 60 | if ( i < IREMAP_ENTRY_NR ) |
278 | 60 | ir_ctrl->iremap_num += nr; |
279 | 60 | return i; |
280 | 60 | } |
281 | | |
282 | | static int remap_entry_to_ioapic_rte( |
283 | | struct iommu *iommu, int index, struct IO_xAPIC_route_entry *old_rte) |
284 | 30 | { |
285 | 30 | struct iremap_entry *iremap_entry = NULL, *iremap_entries; |
286 | 30 | unsigned long flags; |
287 | 30 | struct ir_ctrl *ir_ctrl = iommu_ir_ctrl(iommu); |
288 | 30 | |
289 | 30 | if ( index < 0 || index > IREMAP_ENTRY_NR - 1 ) |
290 | 0 | { |
291 | 0 | dprintk(XENLOG_ERR VTDPREFIX, |
292 | 0 | "IO-APIC index (%d) for remap table is invalid\n", |
293 | 0 | index); |
294 | 0 | return -EFAULT; |
295 | 0 | } |
296 | 30 | |
297 | 30 | spin_lock_irqsave(&ir_ctrl->iremap_lock, flags); |
298 | 30 | |
299 | 30 | GET_IREMAP_ENTRY(ir_ctrl->iremap_maddr, index, |
300 | 30 | iremap_entries, iremap_entry); |
301 | 30 | |
302 | 30 | if ( iremap_entry->val == 0 ) |
303 | 0 | { |
304 | 0 | dprintk(XENLOG_ERR VTDPREFIX, |
305 | 0 | "IO-APIC index (%d) has an empty entry\n", |
306 | 0 | index); |
307 | 0 | unmap_vtd_domain_page(iremap_entries); |
308 | 0 | spin_unlock_irqrestore(&ir_ctrl->iremap_lock, flags); |
309 | 0 | return -EFAULT; |
310 | 0 | } |
311 | 30 | |
312 | 30 | old_rte->vector = iremap_entry->remap.vector; |
313 | 30 | old_rte->delivery_mode = iremap_entry->remap.dlm; |
314 | 30 | old_rte->dest_mode = iremap_entry->remap.dm; |
315 | 30 | old_rte->trigger = iremap_entry->remap.tm; |
316 | 30 | old_rte->__reserved_2 = 0; |
317 | 30 | old_rte->dest.logical.__reserved_1 = 0; |
318 | 30 | old_rte->dest.logical.logical_dest = iremap_entry->remap.dst >> 8; |
319 | 30 | |
320 | 30 | unmap_vtd_domain_page(iremap_entries); |
321 | 30 | spin_unlock_irqrestore(&ir_ctrl->iremap_lock, flags); |
322 | 30 | return 0; |
323 | 30 | } |
324 | | |
325 | | static int ioapic_rte_to_remap_entry(struct iommu *iommu, |
326 | | int apic, unsigned int ioapic_pin, struct IO_xAPIC_route_entry *old_rte, |
327 | | unsigned int rte_upper, unsigned int value) |
328 | 93 | { |
329 | 93 | struct iremap_entry *iremap_entry = NULL, *iremap_entries; |
330 | 93 | struct iremap_entry new_ire; |
331 | 93 | struct IO_APIC_route_remap_entry *remap_rte; |
332 | 93 | struct IO_xAPIC_route_entry new_rte; |
333 | 93 | int index; |
334 | 93 | unsigned long flags; |
335 | 93 | struct ir_ctrl *ir_ctrl = iommu_ir_ctrl(iommu); |
336 | 93 | bool init = false; |
337 | 93 | |
338 | 93 | remap_rte = (struct IO_APIC_route_remap_entry *) old_rte; |
339 | 93 | spin_lock_irqsave(&ir_ctrl->iremap_lock, flags); |
340 | 93 | |
341 | 93 | index = apic_pin_2_ir_idx[apic][ioapic_pin]; |
342 | 93 | if ( index < 0 ) |
343 | 18 | { |
344 | 18 | index = alloc_remap_entry(iommu, 1); |
345 | 18 | if ( index < IREMAP_ENTRY_NR ) |
346 | 18 | apic_pin_2_ir_idx[apic][ioapic_pin] = index; |
347 | 18 | init = true; |
348 | 18 | } |
349 | 93 | |
350 | 93 | if ( index > IREMAP_ENTRY_NR - 1 ) |
351 | 0 | { |
352 | 0 | dprintk(XENLOG_ERR VTDPREFIX, |
353 | 0 | "IO-APIC intremap index (%d) larger than maximum index (%d)\n", |
354 | 0 | index, IREMAP_ENTRY_NR - 1); |
355 | 0 | spin_unlock_irqrestore(&ir_ctrl->iremap_lock, flags); |
356 | 0 | return -EFAULT; |
357 | 0 | } |
358 | 93 | |
359 | 93 | GET_IREMAP_ENTRY(ir_ctrl->iremap_maddr, index, |
360 | 93 | iremap_entries, iremap_entry); |
361 | 93 | |
362 | 93 | new_ire = *iremap_entry; |
363 | 93 | |
364 | 93 | if ( rte_upper ) |
365 | 42 | { |
366 | 42 | if ( x2apic_enabled ) |
367 | 42 | new_ire.remap.dst = value; |
368 | 42 | else |
369 | 0 | new_ire.remap.dst = (value >> 24) << 8; |
370 | 42 | } |
371 | 93 | else |
372 | 51 | { |
373 | 51 | *(((u32 *)&new_rte) + 0) = value; |
374 | 51 | new_ire.remap.fpd = 0; |
375 | 51 | new_ire.remap.dm = new_rte.dest_mode; |
376 | 51 | new_ire.remap.tm = new_rte.trigger; |
377 | 51 | new_ire.remap.dlm = new_rte.delivery_mode; |
378 | 51 | /* Hardware require RH = 1 for LPR delivery mode */ |
379 | 51 | new_ire.remap.rh = (new_ire.remap.dlm == dest_LowestPrio); |
380 | 51 | new_ire.remap.avail = 0; |
381 | 51 | new_ire.remap.res_1 = 0; |
382 | 51 | new_ire.remap.vector = new_rte.vector; |
383 | 51 | new_ire.remap.res_2 = 0; |
384 | 51 | |
385 | 51 | set_ioapic_source_id(IO_APIC_ID(apic), &new_ire); |
386 | 51 | new_ire.remap.res_3 = 0; |
387 | 51 | new_ire.remap.res_4 = 0; |
388 | 51 | new_ire.remap.p = 1; /* finally, set present bit */ |
389 | 51 | |
390 | 51 | /* now construct new ioapic rte entry */ |
391 | 51 | remap_rte->vector = new_rte.vector; |
392 | 51 | remap_rte->delivery_mode = 0; /* has to be 0 for remap format */ |
393 | 51 | remap_rte->index_15 = (index >> 15) & 0x1; |
394 | 51 | remap_rte->index_0_14 = index & 0x7fff; |
395 | 51 | |
396 | 51 | remap_rte->delivery_status = new_rte.delivery_status; |
397 | 51 | remap_rte->polarity = new_rte.polarity; |
398 | 51 | remap_rte->irr = new_rte.irr; |
399 | 51 | remap_rte->trigger = new_rte.trigger; |
400 | 51 | remap_rte->mask = new_rte.mask; |
401 | 51 | remap_rte->reserved = 0; |
402 | 51 | remap_rte->format = 1; /* indicate remap format */ |
403 | 51 | } |
404 | 93 | |
405 | 93 | update_irte(iommu, iremap_entry, &new_ire, !init); |
406 | 93 | iommu_flush_cache_entry(iremap_entry, sizeof(*iremap_entry)); |
407 | 93 | iommu_flush_iec_index(iommu, 0, index); |
408 | 93 | |
409 | 93 | unmap_vtd_domain_page(iremap_entries); |
410 | 93 | spin_unlock_irqrestore(&ir_ctrl->iremap_lock, flags); |
411 | 93 | return 0; |
412 | 93 | } |
413 | | |
414 | | unsigned int io_apic_read_remap_rte( |
415 | | unsigned int apic, unsigned int reg) |
416 | 222 | { |
417 | 222 | unsigned int ioapic_pin = (reg - 0x10) / 2; |
418 | 222 | int index; |
419 | 222 | struct IO_xAPIC_route_entry old_rte = { 0 }; |
420 | 126 | int rte_upper = (reg & 1) ? 1 : 0; |
421 | 222 | struct iommu *iommu = ioapic_to_iommu(IO_APIC_ID(apic)); |
422 | 222 | struct ir_ctrl *ir_ctrl = iommu_ir_ctrl(iommu); |
423 | 222 | |
424 | 222 | if ( !ir_ctrl->iremap_num || |
425 | 30 | ( (index = apic_pin_2_ir_idx[apic][ioapic_pin]) < 0 ) ) |
426 | 192 | return __io_apic_read(apic, reg); |
427 | 222 | |
428 | 30 | old_rte = __ioapic_read_entry(apic, ioapic_pin, 1); |
429 | 30 | |
430 | 30 | if ( remap_entry_to_ioapic_rte(iommu, index, &old_rte) ) |
431 | 0 | return __io_apic_read(apic, reg); |
432 | 30 | |
433 | 30 | if ( rte_upper ) |
434 | 0 | return (*(((u32 *)&old_rte) + 1)); |
435 | 30 | else |
436 | 30 | return (*(((u32 *)&old_rte) + 0)); |
437 | 30 | } |
438 | | |
439 | | void io_apic_write_remap_rte( |
440 | | unsigned int apic, unsigned int reg, unsigned int value) |
441 | 93 | { |
442 | 93 | unsigned int ioapic_pin = (reg - 0x10) / 2; |
443 | 93 | struct IO_xAPIC_route_entry old_rte = { 0 }; |
444 | 93 | struct IO_APIC_route_remap_entry *remap_rte; |
445 | 51 | unsigned int rte_upper = (reg & 1) ? 1 : 0; |
446 | 93 | struct iommu *iommu = ioapic_to_iommu(IO_APIC_ID(apic)); |
447 | 93 | int saved_mask; |
448 | 93 | |
449 | 93 | old_rte = __ioapic_read_entry(apic, ioapic_pin, 1); |
450 | 93 | |
451 | 93 | remap_rte = (struct IO_APIC_route_remap_entry *) &old_rte; |
452 | 93 | |
453 | 93 | /* mask the interrupt while we change the intremap table */ |
454 | 93 | saved_mask = remap_rte->mask; |
455 | 93 | remap_rte->mask = 1; |
456 | 93 | __io_apic_write(apic, reg & ~1, *(u32 *)&old_rte); |
457 | 93 | remap_rte->mask = saved_mask; |
458 | 93 | |
459 | 93 | if ( ioapic_rte_to_remap_entry(iommu, apic, ioapic_pin, |
460 | 93 | &old_rte, rte_upper, value) ) |
461 | 0 | { |
462 | 0 | __io_apic_write(apic, reg, value); |
463 | 0 |
|
464 | 0 | /* Recover the original value of 'mask' bit */ |
465 | 0 | if ( rte_upper ) |
466 | 0 | __io_apic_write(apic, reg & ~1, *(u32 *)&old_rte); |
467 | 0 | } |
468 | 93 | else |
469 | 93 | __ioapic_write_entry(apic, ioapic_pin, 1, old_rte); |
470 | 93 | } |
471 | | |
472 | | static void set_msi_source_id(struct pci_dev *pdev, struct iremap_entry *ire) |
473 | 84 | { |
474 | 84 | u16 seg; |
475 | 84 | u8 bus, devfn, secbus; |
476 | 84 | int ret; |
477 | 84 | |
478 | 84 | if ( !pdev || !ire ) |
479 | 0 | return; |
480 | 84 | |
481 | 84 | seg = pdev->seg; |
482 | 84 | bus = pdev->bus; |
483 | 84 | devfn = pdev->devfn; |
484 | 84 | switch ( pdev->type ) |
485 | 84 | { |
486 | 0 | unsigned int sq; |
487 | 0 |
|
488 | 78 | case DEV_TYPE_PCIe_ENDPOINT: |
489 | 78 | case DEV_TYPE_PCIe_BRIDGE: |
490 | 78 | case DEV_TYPE_PCIe2PCI_BRIDGE: |
491 | 78 | case DEV_TYPE_PCI_HOST_BRIDGE: |
492 | 78 | switch ( pdev->phantom_stride ) |
493 | 78 | { |
494 | 0 | case 1: sq = SQ_13_IGNORE_3; break; |
495 | 0 | case 2: sq = SQ_13_IGNORE_2; break; |
496 | 0 | case 4: sq = SQ_13_IGNORE_1; break; |
497 | 78 | default: sq = SQ_ALL_16; break; |
498 | 78 | } |
499 | 78 | set_ire_sid(ire, SVT_VERIFY_SID_SQ, sq, PCI_BDF2(bus, devfn)); |
500 | 78 | break; |
501 | 78 | |
502 | 6 | case DEV_TYPE_PCI: |
503 | 6 | case DEV_TYPE_LEGACY_PCI_BRIDGE: |
504 | 6 | case DEV_TYPE_PCI2PCIe_BRIDGE: |
505 | 6 | ret = find_upstream_bridge(seg, &bus, &devfn, &secbus); |
506 | 6 | if ( ret == 0 ) /* integrated PCI device */ |
507 | 6 | { |
508 | 6 | set_ire_sid(ire, SVT_VERIFY_SID_SQ, SQ_ALL_16, |
509 | 6 | PCI_BDF2(bus, devfn)); |
510 | 6 | } |
511 | 0 | else if ( ret == 1 ) /* find upstream bridge */ |
512 | 0 | { |
513 | 0 | if ( pdev_type(seg, bus, devfn) == DEV_TYPE_PCIe2PCI_BRIDGE ) |
514 | 0 | set_ire_sid(ire, SVT_VERIFY_BUS, SQ_ALL_16, |
515 | 0 | (bus << 8) | pdev->bus); |
516 | 0 | else |
517 | 0 | set_ire_sid(ire, SVT_VERIFY_SID_SQ, SQ_ALL_16, |
518 | 0 | PCI_BDF2(bus, devfn)); |
519 | 0 | } |
520 | 0 | else |
521 | 0 | dprintk(XENLOG_WARNING VTDPREFIX, |
522 | 6 | "d%d: no upstream bridge for %04x:%02x:%02x.%u\n", |
523 | 0 | pdev->domain->domain_id, |
524 | 0 | seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); |
525 | 6 | break; |
526 | 6 | |
527 | 0 | default: |
528 | 0 | dprintk(XENLOG_WARNING VTDPREFIX, |
529 | 0 | "d%d: unknown(%u): %04x:%02x:%02x.%u\n", |
530 | 0 | pdev->domain->domain_id, pdev->type, |
531 | 0 | seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); |
532 | 0 | break; |
533 | 84 | } |
534 | 84 | } |
535 | | |
536 | | static int remap_entry_to_msi_msg( |
537 | | struct iommu *iommu, struct msi_msg *msg, unsigned int index) |
538 | 42 | { |
539 | 42 | struct iremap_entry *iremap_entry = NULL, *iremap_entries; |
540 | 42 | struct msi_msg_remap_entry *remap_rte; |
541 | 42 | unsigned long flags; |
542 | 42 | struct ir_ctrl *ir_ctrl = iommu_ir_ctrl(iommu); |
543 | 42 | |
544 | 42 | remap_rte = (struct msi_msg_remap_entry *) msg; |
545 | 42 | index += (remap_rte->address_lo.index_15 << 15) | |
546 | 42 | remap_rte->address_lo.index_0_14; |
547 | 42 | |
548 | 42 | if ( index >= IREMAP_ENTRY_NR ) |
549 | 0 | { |
550 | 0 | dprintk(XENLOG_ERR VTDPREFIX, |
551 | 0 | "MSI index (%d) for remap table is invalid\n", |
552 | 0 | index); |
553 | 0 | return -EFAULT; |
554 | 0 | } |
555 | 42 | |
556 | 42 | spin_lock_irqsave(&ir_ctrl->iremap_lock, flags); |
557 | 42 | |
558 | 42 | GET_IREMAP_ENTRY(ir_ctrl->iremap_maddr, index, |
559 | 42 | iremap_entries, iremap_entry); |
560 | 42 | |
561 | 42 | if ( iremap_entry->val == 0 ) |
562 | 0 | { |
563 | 0 | dprintk(XENLOG_ERR VTDPREFIX, |
564 | 0 | "MSI index (%d) has an empty entry\n", |
565 | 0 | index); |
566 | 0 | unmap_vtd_domain_page(iremap_entries); |
567 | 0 | spin_unlock_irqrestore(&ir_ctrl->iremap_lock, flags); |
568 | 0 | return -EFAULT; |
569 | 0 | } |
570 | 42 | |
571 | 42 | msg->address_hi = MSI_ADDR_BASE_HI; |
572 | 42 | msg->address_lo = |
573 | 42 | MSI_ADDR_BASE_LO | |
574 | 42 | ((iremap_entry->remap.dm == 0) ? |
575 | 0 | MSI_ADDR_DESTMODE_PHYS: |
576 | 42 | MSI_ADDR_DESTMODE_LOGIC) | |
577 | 42 | ((iremap_entry->remap.dlm != dest_LowestPrio) ? |
578 | 0 | MSI_ADDR_REDIRECTION_CPU: |
579 | 42 | MSI_ADDR_REDIRECTION_LOWPRI); |
580 | 42 | if ( x2apic_enabled ) |
581 | 42 | msg->dest32 = iremap_entry->remap.dst; |
582 | 42 | else |
583 | 0 | msg->dest32 = (iremap_entry->remap.dst >> 8) & 0xff; |
584 | 42 | msg->address_lo |= MSI_ADDR_DEST_ID(msg->dest32); |
585 | 42 | |
586 | 42 | msg->data = |
587 | 42 | MSI_DATA_TRIGGER_EDGE | |
588 | 42 | MSI_DATA_LEVEL_ASSERT | |
589 | 42 | ((iremap_entry->remap.dlm != dest_LowestPrio) ? |
590 | 0 | MSI_DATA_DELIVERY_FIXED: |
591 | 42 | MSI_DATA_DELIVERY_LOWPRI) | |
592 | 42 | iremap_entry->remap.vector; |
593 | 42 | |
594 | 42 | unmap_vtd_domain_page(iremap_entries); |
595 | 42 | spin_unlock_irqrestore(&ir_ctrl->iremap_lock, flags); |
596 | 42 | return 0; |
597 | 42 | } |
598 | | |
599 | | static int msi_msg_to_remap_entry( |
600 | | struct iommu *iommu, struct pci_dev *pdev, |
601 | | struct msi_desc *msi_desc, struct msi_msg *msg) |
602 | 84 | { |
603 | 84 | struct iremap_entry *iremap_entry = NULL, *iremap_entries, new_ire = { }; |
604 | 84 | struct msi_msg_remap_entry *remap_rte; |
605 | 84 | unsigned int index, i, nr = 1; |
606 | 84 | unsigned long flags; |
607 | 84 | struct ir_ctrl *ir_ctrl = iommu_ir_ctrl(iommu); |
608 | 84 | const struct pi_desc *pi_desc = msi_desc->pi_desc; |
609 | 84 | |
610 | 84 | if ( msi_desc->msi_attrib.type == PCI_CAP_ID_MSI ) |
611 | 12 | nr = msi_desc->msi.nvec; |
612 | 84 | |
613 | 84 | spin_lock_irqsave(&ir_ctrl->iremap_lock, flags); |
614 | 84 | |
615 | 84 | if ( msg == NULL ) |
616 | 0 | { |
617 | 0 | /* Free specified unused IRTEs */ |
618 | 0 | for ( i = 0; i < nr; ++i ) |
619 | 0 | { |
620 | 0 | free_remap_entry(iommu, msi_desc->remap_index + i); |
621 | 0 | msi_desc[i].irte_initialized = false; |
622 | 0 | } |
623 | 0 | spin_unlock_irqrestore(&ir_ctrl->iremap_lock, flags); |
624 | 0 | return 0; |
625 | 0 | } |
626 | 84 | |
627 | 84 | if ( msi_desc->remap_index < 0 ) |
628 | 42 | { |
629 | 42 | index = alloc_remap_entry(iommu, nr); |
630 | 84 | for ( i = 0; i < nr; ++i ) |
631 | 42 | msi_desc[i].remap_index = index + i; |
632 | 42 | } |
633 | 84 | else |
634 | 42 | index = msi_desc->remap_index; |
635 | 84 | |
636 | 84 | if ( index > IREMAP_ENTRY_NR - 1 ) |
637 | 0 | { |
638 | 0 | dprintk(XENLOG_ERR VTDPREFIX, |
639 | 0 | "MSI intremap index (%d) larger than maximum index (%d)!\n", |
640 | 0 | index, IREMAP_ENTRY_NR - 1); |
641 | 0 | for ( i = 0; i < nr; ++i ) |
642 | 0 | msi_desc[i].remap_index = -1; |
643 | 0 | spin_unlock_irqrestore(&ir_ctrl->iremap_lock, flags); |
644 | 0 | return -EFAULT; |
645 | 0 | } |
646 | 84 | |
647 | 84 | GET_IREMAP_ENTRY(ir_ctrl->iremap_maddr, index, |
648 | 84 | iremap_entries, iremap_entry); |
649 | 84 | |
650 | 84 | if ( !pi_desc ) |
651 | 84 | { |
652 | 84 | new_ire.remap.dm = msg->address_lo >> MSI_ADDR_DESTMODE_SHIFT; |
653 | 84 | new_ire.remap.tm = msg->data >> MSI_DATA_TRIGGER_SHIFT; |
654 | 84 | new_ire.remap.dlm = msg->data >> MSI_DATA_DELIVERY_MODE_SHIFT; |
655 | 84 | /* Hardware requires RH = 1 for lowest priority delivery mode */ |
656 | 84 | new_ire.remap.rh = (new_ire.remap.dlm == dest_LowestPrio); |
657 | 84 | new_ire.remap.vector = (msg->data >> MSI_DATA_VECTOR_SHIFT) & |
658 | 84 | MSI_DATA_VECTOR_MASK; |
659 | 84 | if ( x2apic_enabled ) |
660 | 84 | new_ire.remap.dst = msg->dest32; |
661 | 84 | else |
662 | 0 | new_ire.remap.dst = |
663 | 0 | MASK_EXTR(msg->address_lo, MSI_ADDR_DEST_ID_MASK) << 8; |
664 | 84 | new_ire.remap.p = 1; |
665 | 84 | } |
666 | 84 | else |
667 | 0 | { |
668 | 0 | new_ire.post.im = 1; |
669 | 0 | new_ire.post.vector = msi_desc->gvec; |
670 | 0 | new_ire.post.pda_l = virt_to_maddr(pi_desc) >> (32 - PDA_LOW_BIT); |
671 | 0 | new_ire.post.pda_h = virt_to_maddr(pi_desc) >> 32; |
672 | 0 | new_ire.post.p = 1; |
673 | 0 | } |
674 | 84 | |
675 | 84 | if ( pdev ) |
676 | 84 | set_msi_source_id(pdev, &new_ire); |
677 | 84 | else |
678 | 0 | set_hpet_source_id(msi_desc->hpet_id, &new_ire); |
679 | 84 | |
680 | 84 | /* now construct new MSI/MSI-X rte entry */ |
681 | 84 | remap_rte = (struct msi_msg_remap_entry *)msg; |
682 | 84 | remap_rte->address_lo.dontcare = 0; |
683 | 84 | i = index; |
684 | 84 | if ( !nr ) |
685 | 0 | i -= msi_desc->msi_attrib.entry_nr; |
686 | 84 | remap_rte->address_lo.index_15 = (i >> 15) & 0x1; |
687 | 84 | remap_rte->address_lo.index_0_14 = i & 0x7fff; |
688 | 84 | remap_rte->address_lo.SHV = 1; |
689 | 84 | remap_rte->address_lo.format = 1; |
690 | 84 | |
691 | 84 | remap_rte->address_hi = 0; |
692 | 84 | remap_rte->data = index - i; |
693 | 84 | |
694 | 84 | update_irte(iommu, iremap_entry, &new_ire, msi_desc->irte_initialized); |
695 | 84 | msi_desc->irte_initialized = true; |
696 | 84 | |
697 | 84 | iommu_flush_cache_entry(iremap_entry, sizeof(*iremap_entry)); |
698 | 84 | iommu_flush_iec_index(iommu, 0, index); |
699 | 84 | |
700 | 84 | unmap_vtd_domain_page(iremap_entries); |
701 | 84 | spin_unlock_irqrestore(&ir_ctrl->iremap_lock, flags); |
702 | 84 | return 0; |
703 | 84 | } |
704 | | |
705 | | void msi_msg_read_remap_rte( |
706 | | struct msi_desc *msi_desc, struct msi_msg *msg) |
707 | 42 | { |
708 | 42 | struct pci_dev *pdev = msi_desc->dev; |
709 | 42 | struct acpi_drhd_unit *drhd = NULL; |
710 | 42 | |
711 | 42 | drhd = pdev ? acpi_find_matched_drhd_unit(pdev) |
712 | 0 | : hpet_to_drhd(msi_desc->hpet_id); |
713 | 42 | if ( drhd ) |
714 | 42 | remap_entry_to_msi_msg(drhd->iommu, msg, |
715 | 42 | msi_desc->msi_attrib.type == PCI_CAP_ID_MSI |
716 | 36 | ? msi_desc->msi_attrib.entry_nr : 0); |
717 | 42 | } |
718 | | |
719 | | int msi_msg_write_remap_rte( |
720 | | struct msi_desc *msi_desc, struct msi_msg *msg) |
721 | 84 | { |
722 | 84 | struct pci_dev *pdev = msi_desc->dev; |
723 | 84 | struct acpi_drhd_unit *drhd = NULL; |
724 | 84 | |
725 | 84 | drhd = pdev ? acpi_find_matched_drhd_unit(pdev) |
726 | 0 | : hpet_to_drhd(msi_desc->hpet_id); |
727 | 84 | return drhd ? msi_msg_to_remap_entry(drhd->iommu, pdev, msi_desc, msg) |
728 | 0 | : -EINVAL; |
729 | 84 | } |
730 | | |
731 | | int __init intel_setup_hpet_msi(struct msi_desc *msi_desc) |
732 | 0 | { |
733 | 0 | struct iommu *iommu = hpet_to_iommu(msi_desc->hpet_id); |
734 | 0 | struct ir_ctrl *ir_ctrl = iommu_ir_ctrl(iommu); |
735 | 0 | unsigned long flags; |
736 | 0 | int rc = 0; |
737 | 0 |
|
738 | 0 | if ( !ir_ctrl || !ir_ctrl->iremap_maddr ) |
739 | 0 | return 0; |
740 | 0 |
|
741 | 0 | spin_lock_irqsave(&ir_ctrl->iremap_lock, flags); |
742 | 0 | msi_desc->remap_index = alloc_remap_entry(iommu, 1); |
743 | 0 | if ( msi_desc->remap_index >= IREMAP_ENTRY_NR ) |
744 | 0 | { |
745 | 0 | dprintk(XENLOG_ERR VTDPREFIX, |
746 | 0 | "HPET intremap index (%d) larger than maximum index (%d)!\n", |
747 | 0 | msi_desc->remap_index, IREMAP_ENTRY_NR - 1); |
748 | 0 | msi_desc->remap_index = -1; |
749 | 0 | rc = -ENXIO; |
750 | 0 | } |
751 | 0 | spin_unlock_irqrestore(&ir_ctrl->iremap_lock, flags); |
752 | 0 |
|
753 | 0 | return rc; |
754 | 0 | } |
755 | | |
756 | | int enable_intremap(struct iommu *iommu, int eim) |
757 | 2 | { |
758 | 2 | struct acpi_drhd_unit *drhd; |
759 | 2 | struct ir_ctrl *ir_ctrl; |
760 | 2 | u32 sts, gcmd; |
761 | 2 | unsigned long flags; |
762 | 2 | |
763 | 2 | ASSERT(ecap_intr_remap(iommu->ecap) && iommu_intremap); |
764 | 2 | |
765 | 2 | if ( !platform_supports_intremap() ) |
766 | 0 | { |
767 | 0 | printk(XENLOG_ERR VTDPREFIX |
768 | 0 | " Platform firmware does not support interrupt remapping\n"); |
769 | 0 | return -EINVAL; |
770 | 0 | } |
771 | 2 | |
772 | 2 | ir_ctrl = iommu_ir_ctrl(iommu); |
773 | 2 | sts = dmar_readl(iommu->reg, DMAR_GSTS_REG); |
774 | 2 | |
775 | 2 | /* Return if already enabled by Xen */ |
776 | 2 | if ( (sts & DMA_GSTS_IRES) && ir_ctrl->iremap_maddr ) |
777 | 1 | return 0; |
778 | 2 | |
779 | 1 | if ( !(sts & DMA_GSTS_QIES) ) |
780 | 0 | { |
781 | 0 | printk(XENLOG_ERR VTDPREFIX |
782 | 0 | " Queued invalidation is not enabled on IOMMU #%u:" |
783 | 0 | " Should not enable interrupt remapping\n", iommu->index); |
784 | 0 | return -EINVAL; |
785 | 0 | } |
786 | 1 | |
787 | 1 | if ( !eim && (sts & DMA_GSTS_CFIS) ) |
788 | 0 | printk(XENLOG_WARNING VTDPREFIX |
789 | 0 | " Compatibility Format Interrupts permitted on IOMMU #%u:" |
790 | 0 | " Device pass-through will be insecure\n", iommu->index); |
791 | 1 | |
792 | 1 | if ( ir_ctrl->iremap_maddr == 0 ) |
793 | 1 | { |
794 | 1 | drhd = iommu_to_drhd(iommu); |
795 | 1 | ir_ctrl->iremap_maddr = alloc_pgtable_maddr(drhd, IREMAP_ARCH_PAGE_NR); |
796 | 1 | if ( ir_ctrl->iremap_maddr == 0 ) |
797 | 0 | { |
798 | 0 | dprintk(XENLOG_WARNING VTDPREFIX, |
799 | 0 | "Cannot allocate memory for ir_ctrl->iremap_maddr\n"); |
800 | 0 | return -ENOMEM; |
801 | 0 | } |
802 | 1 | ir_ctrl->iremap_num = 0; |
803 | 1 | } |
804 | 1 | |
805 | 1 | /* set extended interrupt mode bit */ |
806 | 1 | ir_ctrl->iremap_maddr |= eim ? IRTA_EIME : 0; |
807 | 1 | |
808 | 1 | spin_lock_irqsave(&iommu->register_lock, flags); |
809 | 1 | |
810 | 1 | /* set size of the interrupt remapping table */ |
811 | 1 | ir_ctrl->iremap_maddr |= IRTA_REG_TABLE_SIZE; |
812 | 1 | dmar_writeq(iommu->reg, DMAR_IRTA_REG, ir_ctrl->iremap_maddr); |
813 | 1 | |
814 | 1 | /* set SIRTP */ |
815 | 1 | gcmd = dmar_readl(iommu->reg, DMAR_GSTS_REG); |
816 | 1 | gcmd |= DMA_GCMD_SIRTP; |
817 | 1 | dmar_writel(iommu->reg, DMAR_GCMD_REG, gcmd); |
818 | 1 | |
819 | 1 | IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, dmar_readl, |
820 | 1 | (sts & DMA_GSTS_SIRTPS), sts); |
821 | 1 | spin_unlock_irqrestore(&iommu->register_lock, flags); |
822 | 1 | |
823 | 1 | /* After set SIRTP, must globally invalidate the interrupt entry cache */ |
824 | 1 | iommu_flush_iec_global(iommu); |
825 | 1 | |
826 | 1 | spin_lock_irqsave(&iommu->register_lock, flags); |
827 | 1 | /* enable interrupt remapping hardware */ |
828 | 1 | gcmd |= DMA_GCMD_IRE; |
829 | 1 | dmar_writel(iommu->reg, DMAR_GCMD_REG, gcmd); |
830 | 1 | |
831 | 1 | IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, dmar_readl, |
832 | 1 | (sts & DMA_GSTS_IRES), sts); |
833 | 1 | spin_unlock_irqrestore(&iommu->register_lock, flags); |
834 | 1 | |
835 | 1 | return init_apic_pin_2_ir_idx(); |
836 | 1 | } |
837 | | |
838 | | void disable_intremap(struct iommu *iommu) |
839 | 1 | { |
840 | 1 | u32 sts; |
841 | 1 | u64 irta; |
842 | 1 | unsigned long flags; |
843 | 1 | |
844 | 1 | if ( !ecap_intr_remap(iommu->ecap) ) |
845 | 0 | return; |
846 | 1 | |
847 | 1 | spin_lock_irqsave(&iommu->register_lock, flags); |
848 | 1 | sts = dmar_readl(iommu->reg, DMAR_GSTS_REG); |
849 | 1 | if ( !(sts & DMA_GSTS_IRES) ) |
850 | 1 | goto out; |
851 | 1 | |
852 | 0 | dmar_writel(iommu->reg, DMAR_GCMD_REG, sts & (~DMA_GCMD_IRE)); |
853 | 0 |
|
854 | 0 | IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, dmar_readl, |
855 | 0 | !(sts & DMA_GSTS_IRES), sts); |
856 | 0 |
|
857 | 0 | /* If we are disabling Interrupt Remapping, make sure we dont stay in |
858 | 0 | * Extended Interrupt Mode, as this is unaffected by the Interrupt |
859 | 0 | * Remapping flag in each DMAR Global Control Register. |
860 | 0 | * Specifically, local apics in xapic mode do not like interrupts delivered |
861 | 0 | * in x2apic mode. Any code turning interrupt remapping back on will set |
862 | 0 | * EIME back correctly. |
863 | 0 | */ |
864 | 0 | if ( !ecap_eim(iommu->ecap) ) |
865 | 0 | goto out; |
866 | 0 |
|
867 | 0 | /* Can't read the register unless we ecaps says we can */ |
868 | 0 | irta = dmar_readl(iommu->reg, DMAR_IRTA_REG); |
869 | 0 | if ( !(irta & IRTA_EIME) ) |
870 | 0 | goto out; |
871 | 0 |
|
872 | 0 | dmar_writel(iommu->reg, DMAR_IRTA_REG, irta & ~IRTA_EIME); |
873 | 0 | IOMMU_WAIT_OP(iommu, DMAR_IRTA_REG, dmar_readl, |
874 | 0 | !(irta & IRTA_EIME), irta); |
875 | 0 |
|
876 | 1 | out: |
877 | 1 | spin_unlock_irqrestore(&iommu->register_lock, flags); |
878 | 1 | } |
879 | | |
880 | | /* |
881 | | * This function is used to enable Interrupt remapping when |
882 | | * enable x2apic |
883 | | */ |
884 | | int iommu_enable_x2apic_IR(void) |
885 | 1 | { |
886 | 1 | struct acpi_drhd_unit *drhd; |
887 | 1 | struct iommu *iommu; |
888 | 1 | |
889 | 1 | if ( system_state < SYS_STATE_active ) |
890 | 1 | { |
891 | 1 | if ( !iommu_supports_eim() ) |
892 | 0 | return -EOPNOTSUPP; |
893 | 1 | |
894 | 1 | if ( !platform_supports_x2apic() ) |
895 | 0 | return -ENXIO; |
896 | 1 | } |
897 | 0 | else if ( !x2apic_enabled ) |
898 | 0 | return -EOPNOTSUPP; |
899 | 1 | |
900 | 1 | for_each_drhd_unit ( drhd ) |
901 | 1 | { |
902 | 1 | iommu = drhd->iommu; |
903 | 1 | |
904 | 1 | /* Clear previous faults */ |
905 | 1 | clear_fault_bits(iommu); |
906 | 1 | |
907 | 1 | /* |
908 | 1 | * Disable interrupt remapping and queued invalidation if |
909 | 1 | * already enabled by BIOS |
910 | 1 | */ |
911 | 1 | disable_intremap(iommu); |
912 | 1 | disable_qinval(iommu); |
913 | 1 | } |
914 | 1 | |
915 | 1 | /* Enable queue invalidation */ |
916 | 1 | for_each_drhd_unit ( drhd ) |
917 | 1 | { |
918 | 1 | iommu = drhd->iommu; |
919 | 1 | if ( enable_qinval(iommu) != 0 ) |
920 | 0 | { |
921 | 0 | dprintk(XENLOG_INFO VTDPREFIX, |
922 | 0 | "Failed to enable Queued Invalidation!\n"); |
923 | 0 | return -EIO; |
924 | 0 | } |
925 | 1 | } |
926 | 1 | |
927 | 1 | /* Enable interrupt remapping */ |
928 | 1 | for_each_drhd_unit ( drhd ) |
929 | 1 | { |
930 | 1 | iommu = drhd->iommu; |
931 | 1 | if ( enable_intremap(iommu, 1) ) |
932 | 0 | { |
933 | 0 | dprintk(XENLOG_INFO VTDPREFIX, |
934 | 0 | "Failed to enable Interrupt Remapping!\n"); |
935 | 0 | return -EIO; |
936 | 0 | } |
937 | 1 | } |
938 | 1 | |
939 | 1 | return 0; |
940 | 1 | } |
941 | | |
942 | | /* |
943 | | * This function is used to disable Interrutp remapping when |
944 | | * suspend local apic |
945 | | */ |
946 | | void iommu_disable_x2apic_IR(void) |
947 | 0 | { |
948 | 0 | struct acpi_drhd_unit *drhd; |
949 | 0 |
|
950 | 0 | /* x2apic_enabled implies iommu_supports_eim(). */ |
951 | 0 | if ( !x2apic_enabled ) |
952 | 0 | return; |
953 | 0 |
|
954 | 0 | for_each_drhd_unit ( drhd ) |
955 | 0 | disable_intremap(drhd->iommu); |
956 | 0 |
|
957 | 0 | for_each_drhd_unit ( drhd ) |
958 | 0 | disable_qinval(drhd->iommu); |
959 | 0 | } |
960 | | |
961 | | /* |
962 | | * This function is used to update the IRTE for posted-interrupt |
963 | | * when guest changes MSI/MSI-X information. |
964 | | */ |
965 | | int pi_update_irte(const struct pi_desc *pi_desc, const struct pirq *pirq, |
966 | | const uint8_t gvec) |
967 | 0 | { |
968 | 0 | struct irq_desc *desc; |
969 | 0 | struct msi_desc *msi_desc; |
970 | 0 | int rc; |
971 | 0 |
|
972 | 0 | desc = pirq_spin_lock_irq_desc(pirq, NULL); |
973 | 0 | if ( !desc ) |
974 | 0 | return -EINVAL; |
975 | 0 |
|
976 | 0 | msi_desc = desc->msi_desc; |
977 | 0 | if ( !msi_desc ) |
978 | 0 | { |
979 | 0 | rc = -ENODEV; |
980 | 0 | goto unlock_out; |
981 | 0 | } |
982 | 0 | msi_desc->pi_desc = pi_desc; |
983 | 0 | msi_desc->gvec = gvec; |
984 | 0 |
|
985 | 0 | spin_unlock_irq(&desc->lock); |
986 | 0 |
|
987 | 0 | ASSERT(pcidevs_locked()); |
988 | 0 | return iommu_update_ire_from_msi(msi_desc, &msi_desc->msg); |
989 | 0 |
|
990 | 0 | unlock_out: |
991 | 0 | spin_unlock_irq(&desc->lock); |
992 | 0 |
|
993 | 0 | return rc; |
994 | 0 | } |