1 /*
2 * Intel IOMMU unit test.
3 *
4 * Copyright (C) 2016 Red Hat, Inc.
5 *
6 * Authors:
7 * Peter Xu <peterx@redhat.com>,
8 *
9 * This work is licensed under the terms of the GNU LGPL, version 2 or
10 * later.
11 */
12
13 #include "intel-iommu.h"
14 #include "pci-edu.h"
15 #include "x86/apic.h"
16 #include "vm.h"
17 #include "alloc_page.h"
18
19 #define VTD_TEST_DMAR_4B ("DMAR 4B memcpy test")
20 #define VTD_TEST_IR_MSI ("IR MSI")
21 #define VTD_TEST_IR_IOAPIC ("IR IOAPIC")
22
23 static struct pci_edu_dev edu_dev;
24
vtd_test_dmar(void)25 static void vtd_test_dmar(void)
26 {
27 struct pci_edu_dev *dev = &edu_dev;
28 void *page = alloc_page();
29
30 report_prefix_push("vtd_dmar");
31
32 #define DMA_TEST_WORD (0x12345678)
33 /* Modify the first 4 bytes of the page */
34 *(uint32_t *)page = DMA_TEST_WORD;
35
36 /*
37 * Map the newly allocated page into IOVA address 0 (size 4K)
38 * of the device address space. Root entry and context entry
39 * will be automatically created when needed.
40 */
41 vtd_map_range(dev->pci_dev.bdf, 0, virt_to_phys(page), PAGE_SIZE);
42
43 /*
44 * DMA the first 4 bytes of the page to EDU device buffer
45 * offset 0.
46 */
47 edu_dma(dev, 0, 4, 0, false);
48
49 /*
50 * DMA the first 4 bytes of EDU device buffer into the page
51 * with offset 4 (so it'll be using 4-7 bytes).
52 */
53 edu_dma(dev, 4, 4, 0, true);
54
55 /*
56 * Check data match between 0-3 bytes and 4-7 bytes of the
57 * page.
58 */
59 report(*((uint32_t *)page + 1) == DMA_TEST_WORD, VTD_TEST_DMAR_4B);
60
61 free_page(page);
62
63 report_prefix_pop();
64 }
65
66 static volatile bool edu_intr_recved;
67
edu_isr(isr_regs_t * regs)68 static void edu_isr(isr_regs_t *regs)
69 {
70 edu_intr_recved = true;
71 eoi();
72 edu_reg_writel(&edu_dev, EDU_REG_INTR_ACK,
73 edu_reg_readl(&edu_dev, EDU_REG_INTR_STATUS));
74 }
75
vtd_test_ir(void)76 static void vtd_test_ir(void)
77 {
78 #define VTD_TEST_VECTOR_IOAPIC (0xed)
79 #define VTD_TEST_VECTOR_MSI (0xee)
80 struct pci_edu_dev *dev = &edu_dev;
81 struct pci_dev *pci_dev = &dev->pci_dev;
82
83 report_prefix_push("vtd_ir");
84
85 sti();
86
87 /* This will enable INTx */
88 pci_msi_set_enable(pci_dev, false);
89 vtd_setup_ioapic_irq(pci_dev, VTD_TEST_VECTOR_IOAPIC,
90 0, TRIGGER_EDGE);
91 handle_irq(VTD_TEST_VECTOR_IOAPIC, edu_isr);
92
93 edu_intr_recved = false;
94 wmb();
95 /* Manually trigger INTR */
96 edu_reg_writel(dev, EDU_REG_INTR_RAISE, 1);
97
98 while (!edu_intr_recved)
99 cpu_relax();
100
101 /* Clear INTR bits */
102 edu_reg_writel(dev, EDU_REG_INTR_RAISE, 0);
103
104 /* We are good as long as we reach here */
105 report(edu_intr_recved == true, VTD_TEST_IR_IOAPIC);
106
107 /*
108 * Setup EDU PCI device MSI, using interrupt remapping. By
109 * default, EDU device is using INTx.
110 */
111 if (!vtd_setup_msi(pci_dev, VTD_TEST_VECTOR_MSI, 0)) {
112 printf("edu device does not support MSI, skip test\n");
113 report_skip(VTD_TEST_IR_MSI);
114 return;
115 }
116
117 handle_irq(VTD_TEST_VECTOR_MSI, edu_isr);
118
119 edu_intr_recved = false;
120 wmb();
121 /* Manually trigger INTR */
122 edu_reg_writel(dev, EDU_REG_INTR_RAISE, 1);
123
124 while (!edu_intr_recved)
125 cpu_relax();
126
127 /* We are good as long as we reach here */
128 report(edu_intr_recved == true, VTD_TEST_IR_MSI);
129
130 report_prefix_pop();
131 }
132
main(int argc,char * argv[])133 int main(int argc, char *argv[])
134 {
135 setup_vm();
136
137 vtd_init();
138
139 report_prefix_push("vtd_init");
140
141 report(vtd_readl(DMAR_FSTS_REG) == 0, "fault status check");
142 report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_QI, "QI enablement");
143 report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_ROOT, "DMAR table setup");
144 report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_IR_TABLE, "IR table setup");
145 report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_DMAR, "DMAR enablement");
146 report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_IR, "IR enablement");
147 report(vtd_readq(DMAR_CAP_REG) & VTD_CAP_SAGAW,
148 "DMAR support 39 bits address width");
149 report(vtd_readq(DMAR_CAP_REG) & VTD_CAP_SLLPS,
150 "DMAR support huge pages");
151
152 report_prefix_pop();
153
154 if (!edu_init(&edu_dev)) {
155 printf("Please specify \"-device edu\" to do "
156 "further IOMMU tests.\n");
157 report_skip(VTD_TEST_DMAR_4B);
158 report_skip(VTD_TEST_IR_IOAPIC);
159 report_skip(VTD_TEST_IR_MSI);
160 } else {
161 printf("Found EDU device:\n");
162 pci_dev_print(&edu_dev.pci_dev);
163 vtd_test_dmar();
164 vtd_test_ir();
165 }
166
167 return report_summary();
168 }
169