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