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