xref: /kvm-unit-tests/x86/intel-iommu.c (revision cd5f2fb4ad641c51fe0f1a85264dc3f6ede6e131)
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