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