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