/root/src/xen/xen/drivers/passthrough/vtd/utils.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 | | */ |
18 | | |
19 | | #include <xen/sched.h> |
20 | | #include <xen/delay.h> |
21 | | #include <xen/iommu.h> |
22 | | #include <xen/time.h> |
23 | | #include <xen/pci.h> |
24 | | #include <xen/pci_regs.h> |
25 | | #include "iommu.h" |
26 | | #include "dmar.h" |
27 | | #include "vtd.h" |
28 | | #include "extern.h" |
29 | | #include <asm/io_apic.h> |
30 | | |
31 | | /* Disable vt-d protected memory registers. */ |
32 | | void disable_pmr(struct iommu *iommu) |
33 | 1 | { |
34 | 1 | u32 val; |
35 | 1 | unsigned long flags; |
36 | 1 | |
37 | 1 | val = dmar_readl(iommu->reg, DMAR_PMEN_REG); |
38 | 1 | if ( !(val & DMA_PMEN_PRS) ) |
39 | 1 | return; |
40 | 1 | |
41 | 0 | spin_lock_irqsave(&iommu->register_lock, flags); |
42 | 0 | dmar_writel(iommu->reg, DMAR_PMEN_REG, val & ~DMA_PMEN_EPM); |
43 | 0 |
|
44 | 0 | IOMMU_WAIT_OP(iommu, DMAR_PMEN_REG, dmar_readl, |
45 | 0 | !(val & DMA_PMEN_PRS), val); |
46 | 0 | spin_unlock_irqrestore(&iommu->register_lock, flags); |
47 | 0 |
|
48 | 0 | dprintk(XENLOG_INFO VTDPREFIX, |
49 | 0 | "Disabled protected memory registers\n"); |
50 | 0 | } |
51 | | |
52 | | void print_iommu_regs(struct acpi_drhd_unit *drhd) |
53 | 0 | { |
54 | 0 | struct iommu *iommu = drhd->iommu; |
55 | 0 | u64 cap; |
56 | 0 |
|
57 | 0 | printk("---- print_iommu_regs ----\n"); |
58 | 0 | printk(" drhd->address = %"PRIx64"\n", drhd->address); |
59 | 0 | printk(" VER = %x\n", dmar_readl(iommu->reg, DMAR_VER_REG)); |
60 | 0 | printk(" CAP = %"PRIx64"\n", cap = dmar_readq(iommu->reg, DMAR_CAP_REG)); |
61 | 0 | printk(" n_fault_reg = %"PRIx64"\n", cap_num_fault_regs(cap)); |
62 | 0 | printk(" fault_recording_offset = %"PRIx64"\n", cap_fault_reg_offset(cap)); |
63 | 0 | if ( cap_fault_reg_offset(cap) < PAGE_SIZE ) |
64 | 0 | { |
65 | 0 | printk(" fault_recording_reg_l = %"PRIx64"\n", |
66 | 0 | dmar_readq(iommu->reg, cap_fault_reg_offset(cap))); |
67 | 0 | printk(" fault_recording_reg_h = %"PRIx64"\n", |
68 | 0 | dmar_readq(iommu->reg, cap_fault_reg_offset(cap) + 8)); |
69 | 0 | } |
70 | 0 | printk(" ECAP = %"PRIx64"\n", dmar_readq(iommu->reg, DMAR_ECAP_REG)); |
71 | 0 | printk(" GCMD = %x\n", dmar_readl(iommu->reg, DMAR_GCMD_REG)); |
72 | 0 | printk(" GSTS = %x\n", dmar_readl(iommu->reg, DMAR_GSTS_REG)); |
73 | 0 | printk(" RTADDR = %"PRIx64"\n", dmar_readq(iommu->reg,DMAR_RTADDR_REG)); |
74 | 0 | printk(" CCMD = %"PRIx64"\n", dmar_readq(iommu->reg, DMAR_CCMD_REG)); |
75 | 0 | printk(" FSTS = %x\n", dmar_readl(iommu->reg, DMAR_FSTS_REG)); |
76 | 0 | printk(" FECTL = %x\n", dmar_readl(iommu->reg, DMAR_FECTL_REG)); |
77 | 0 | printk(" FEDATA = %x\n", dmar_readl(iommu->reg, DMAR_FEDATA_REG)); |
78 | 0 | printk(" FEADDR = %x\n", dmar_readl(iommu->reg, DMAR_FEADDR_REG)); |
79 | 0 | printk(" FEUADDR = %x\n", dmar_readl(iommu->reg, DMAR_FEUADDR_REG)); |
80 | 0 | } |
81 | | |
82 | | static u32 get_level_index(unsigned long gmfn, int level) |
83 | 0 | { |
84 | 0 | while ( --level ) |
85 | 0 | gmfn = gmfn >> LEVEL_STRIDE; |
86 | 0 |
|
87 | 0 | return gmfn & LEVEL_MASK; |
88 | 0 | } |
89 | | |
90 | | void print_vtd_entries(struct iommu *iommu, int bus, int devfn, u64 gmfn) |
91 | 0 | { |
92 | 0 | struct context_entry *ctxt_entry; |
93 | 0 | struct root_entry *root_entry; |
94 | 0 | struct dma_pte pte; |
95 | 0 | u64 *l, val; |
96 | 0 | u32 l_index, level; |
97 | 0 |
|
98 | 0 | printk("print_vtd_entries: iommu #%u dev %04x:%02x:%02x.%u gmfn %"PRI_gfn"\n", |
99 | 0 | iommu->index, iommu->intel->drhd->segment, bus, |
100 | 0 | PCI_SLOT(devfn), PCI_FUNC(devfn), gmfn); |
101 | 0 |
|
102 | 0 | if ( iommu->root_maddr == 0 ) |
103 | 0 | { |
104 | 0 | printk(" iommu->root_maddr = 0\n"); |
105 | 0 | return; |
106 | 0 | } |
107 | 0 |
|
108 | 0 | root_entry = (struct root_entry *)map_vtd_domain_page(iommu->root_maddr); |
109 | 0 | if ( root_entry == NULL ) |
110 | 0 | { |
111 | 0 | printk(" root_entry == NULL\n"); |
112 | 0 | return; |
113 | 0 | } |
114 | 0 |
|
115 | 0 | printk(" root_entry[%02x] = %"PRIx64"\n", bus, root_entry[bus].val); |
116 | 0 | if ( !root_present(root_entry[bus]) ) |
117 | 0 | { |
118 | 0 | unmap_vtd_domain_page(root_entry); |
119 | 0 | printk(" root_entry[%02x] not present\n", bus); |
120 | 0 | return; |
121 | 0 | } |
122 | 0 |
|
123 | 0 | val = root_entry[bus].val; |
124 | 0 | unmap_vtd_domain_page(root_entry); |
125 | 0 | ctxt_entry = map_vtd_domain_page(val); |
126 | 0 | if ( ctxt_entry == NULL ) |
127 | 0 | { |
128 | 0 | printk(" ctxt_entry == NULL\n"); |
129 | 0 | return; |
130 | 0 | } |
131 | 0 |
|
132 | 0 | val = ctxt_entry[devfn].lo; |
133 | 0 | printk(" context[%02x] = %"PRIx64"_%"PRIx64"\n", |
134 | 0 | devfn, ctxt_entry[devfn].hi, val); |
135 | 0 | if ( !context_present(ctxt_entry[devfn]) ) |
136 | 0 | { |
137 | 0 | unmap_vtd_domain_page(ctxt_entry); |
138 | 0 | printk(" ctxt_entry[%02x] not present\n", devfn); |
139 | 0 | return; |
140 | 0 | } |
141 | 0 |
|
142 | 0 | level = agaw_to_level(context_address_width(ctxt_entry[devfn])); |
143 | 0 | unmap_vtd_domain_page(ctxt_entry); |
144 | 0 | if ( level != VTD_PAGE_TABLE_LEVEL_3 && |
145 | 0 | level != VTD_PAGE_TABLE_LEVEL_4) |
146 | 0 | { |
147 | 0 | printk("Unsupported VTD page table level (%d)!\n", level); |
148 | 0 | return; |
149 | 0 | } |
150 | 0 |
|
151 | 0 | do |
152 | 0 | { |
153 | 0 | l = map_vtd_domain_page(val); |
154 | 0 | if ( l == NULL ) |
155 | 0 | { |
156 | 0 | printk(" l%u == NULL\n", level); |
157 | 0 | break; |
158 | 0 | } |
159 | 0 | l_index = get_level_index(gmfn, level); |
160 | 0 | pte.val = l[l_index]; |
161 | 0 | unmap_vtd_domain_page(l); |
162 | 0 | printk(" l%u[%03x] = %"PRIx64"\n", level, l_index, pte.val); |
163 | 0 |
|
164 | 0 | if ( !dma_pte_present(pte) ) |
165 | 0 | { |
166 | 0 | printk(" l%u[%03x] not present\n", level, l_index); |
167 | 0 | break; |
168 | 0 | } |
169 | 0 | if ( dma_pte_superpage(pte) ) |
170 | 0 | break; |
171 | 0 | val = dma_pte_addr(pte); |
172 | 0 | } while ( --level ); |
173 | 0 | } |
174 | | |
175 | | void vtd_dump_iommu_info(unsigned char key) |
176 | 0 | { |
177 | 0 | struct acpi_drhd_unit *drhd; |
178 | 0 | struct iommu *iommu; |
179 | 0 | int i; |
180 | 0 |
|
181 | 0 | for_each_drhd_unit ( drhd ) |
182 | 0 | { |
183 | 0 | u32 status = 0; |
184 | 0 |
|
185 | 0 | iommu = drhd->iommu; |
186 | 0 | printk("\niommu %x: nr_pt_levels = %x.\n", iommu->index, |
187 | 0 | iommu->nr_pt_levels); |
188 | 0 |
|
189 | 0 | if ( ecap_queued_inval(iommu->ecap) || ecap_intr_remap(iommu->ecap) ) |
190 | 0 | status = dmar_readl(iommu->reg, DMAR_GSTS_REG); |
191 | 0 |
|
192 | 0 | printk(" Queued Invalidation: %ssupported%s.\n", |
193 | 0 | ecap_queued_inval(iommu->ecap) ? "" : "not ", |
194 | 0 | (status & DMA_GSTS_QIES) ? " and enabled" : "" ); |
195 | 0 |
|
196 | 0 |
|
197 | 0 | printk(" Interrupt Remapping: %ssupported%s.\n", |
198 | 0 | ecap_intr_remap(iommu->ecap) ? "" : "not ", |
199 | 0 | (status & DMA_GSTS_IRES) ? " and enabled" : "" ); |
200 | 0 |
|
201 | 0 | printk(" Interrupt Posting: %ssupported.\n", |
202 | 0 | cap_intr_post(iommu->cap) ? "" : "not "); |
203 | 0 |
|
204 | 0 | if ( status & DMA_GSTS_IRES ) |
205 | 0 | { |
206 | 0 | /* Dump interrupt remapping table. */ |
207 | 0 | u64 iremap_maddr = dmar_readq(iommu->reg, DMAR_IRTA_REG); |
208 | 0 | int nr_entry = 1 << ((iremap_maddr & 0xF) + 1); |
209 | 0 | struct iremap_entry *iremap_entries = NULL; |
210 | 0 | int print_cnt = 0; |
211 | 0 |
|
212 | 0 | printk(" Interrupt remapping table (nr_entry=%#x. " |
213 | 0 | "Only dump P=1 entries here):\n", nr_entry); |
214 | 0 | printk("R means remapped format, P means posted format.\n"); |
215 | 0 | printk("R: SVT SQ SID V AVL FPD DST DLM TM RH DM P\n"); |
216 | 0 | printk("P: SVT SQ SID V AVL FPD PDA URG P\n"); |
217 | 0 | for ( i = 0; i < nr_entry; i++ ) |
218 | 0 | { |
219 | 0 | struct iremap_entry *p; |
220 | 0 | if ( i % (1 << IREMAP_ENTRY_ORDER) == 0 ) |
221 | 0 | { |
222 | 0 | /* This entry across page boundry */ |
223 | 0 | if ( iremap_entries ) |
224 | 0 | unmap_vtd_domain_page(iremap_entries); |
225 | 0 |
|
226 | 0 | GET_IREMAP_ENTRY(iremap_maddr, i, |
227 | 0 | iremap_entries, p); |
228 | 0 | } |
229 | 0 | else |
230 | 0 | p = &iremap_entries[i % (1 << IREMAP_ENTRY_ORDER)]; |
231 | 0 |
|
232 | 0 | if ( !p->remap.p ) |
233 | 0 | continue; |
234 | 0 | if ( !p->remap.im ) |
235 | 0 | printk("R: %04x: %x %x %04x %02x %x %x %08x %x %x %x %x %x\n", |
236 | 0 | i, |
237 | 0 | p->remap.svt, p->remap.sq, p->remap.sid, |
238 | 0 | p->remap.vector, p->remap.avail, p->remap.fpd, |
239 | 0 | p->remap.dst, p->remap.dlm, p->remap.tm, p->remap.rh, |
240 | 0 | p->remap.dm, p->remap.p); |
241 | 0 | else |
242 | 0 | printk("P: %04x: %x %x %04x %02x %x %x %16lx %x %x\n", |
243 | 0 | i, |
244 | 0 | p->post.svt, p->post.sq, p->post.sid, p->post.vector, |
245 | 0 | p->post.avail, p->post.fpd, |
246 | 0 | ((u64)p->post.pda_h << 32) | (p->post.pda_l << 6), |
247 | 0 | p->post.urg, p->post.p); |
248 | 0 |
|
249 | 0 | print_cnt++; |
250 | 0 | } |
251 | 0 | if ( iremap_entries ) |
252 | 0 | unmap_vtd_domain_page(iremap_entries); |
253 | 0 | if ( iommu_ir_ctrl(iommu)->iremap_num != print_cnt ) |
254 | 0 | printk("Warning: Print %d IRTE (actually have %d)!\n", |
255 | 0 | print_cnt, iommu_ir_ctrl(iommu)->iremap_num); |
256 | 0 |
|
257 | 0 | } |
258 | 0 | } |
259 | 0 |
|
260 | 0 | /* Dump the I/O xAPIC redirection table(s). */ |
261 | 0 | if ( iommu_enabled ) |
262 | 0 | { |
263 | 0 | int apic; |
264 | 0 | union IO_APIC_reg_01 reg_01; |
265 | 0 | struct IO_APIC_route_remap_entry *remap; |
266 | 0 | struct ir_ctrl *ir_ctrl; |
267 | 0 |
|
268 | 0 | for ( apic = 0; apic < nr_ioapics; apic++ ) |
269 | 0 | { |
270 | 0 | iommu = ioapic_to_iommu(mp_ioapics[apic].mpc_apicid); |
271 | 0 | ir_ctrl = iommu_ir_ctrl(iommu); |
272 | 0 | if ( !ir_ctrl || !ir_ctrl->iremap_maddr || !ir_ctrl->iremap_num ) |
273 | 0 | continue; |
274 | 0 |
|
275 | 0 | printk( "\nRedirection table of IOAPIC %x:\n", apic); |
276 | 0 |
|
277 | 0 | /* IO xAPIC Version Register. */ |
278 | 0 | reg_01.raw = __io_apic_read(apic, 1); |
279 | 0 |
|
280 | 0 | printk(" #entry IDX FMT MASK TRIG IRR POL STAT DELI VECTOR\n"); |
281 | 0 | for ( i = 0; i <= reg_01.bits.entries; i++ ) |
282 | 0 | { |
283 | 0 | struct IO_APIC_route_entry rte = |
284 | 0 | __ioapic_read_entry(apic, i, TRUE); |
285 | 0 |
|
286 | 0 | remap = (struct IO_APIC_route_remap_entry *) &rte; |
287 | 0 | if ( !remap->format ) |
288 | 0 | continue; |
289 | 0 |
|
290 | 0 | printk(" %02x: %04x %x %x %x %x %x %x" |
291 | 0 | " %x %02x\n", i, |
292 | 0 | remap->index_0_14 | (remap->index_15 << 15), |
293 | 0 | remap->format, remap->mask, remap->trigger, remap->irr, |
294 | 0 | remap->polarity, remap->delivery_status, remap->delivery_mode, |
295 | 0 | remap->vector); |
296 | 0 | } |
297 | 0 | } |
298 | 0 | } |
299 | 0 | } |
300 | | |
301 | | /* |
302 | | * Local variables: |
303 | | * mode: C |
304 | | * c-file-style: "BSD" |
305 | | * c-basic-offset: 4 |
306 | | * indent-tabs-mode: nil |
307 | | * End: |
308 | | */ |