xref: /kvm-unit-tests/x86/intel-iommu.c (revision 3223ade219a6e1b927307022f4800832f24f5c86)
116c0b05fSPeter Xu /*
216c0b05fSPeter Xu  * Intel IOMMU unit test.
316c0b05fSPeter Xu  *
416c0b05fSPeter Xu  * Copyright (C) 2016 Red Hat, Inc.
516c0b05fSPeter Xu  *
616c0b05fSPeter Xu  * Authors:
716c0b05fSPeter Xu  *   Peter Xu <peterx@redhat.com>,
816c0b05fSPeter Xu  *
916c0b05fSPeter Xu  * This work is licensed under the terms of the GNU LGPL, version 2 or
1016c0b05fSPeter Xu  * later.
1116c0b05fSPeter Xu  */
1216c0b05fSPeter Xu 
1316c0b05fSPeter Xu #include "intel-iommu.h"
1492d2c192SPeter Xu #include "pci-edu.h"
157f2477c2SPeter Xu #include "x86/apic.h"
1692d2c192SPeter Xu 
1792d2c192SPeter Xu #define VTD_TEST_DMAR_4B ("DMAR 4B memcpy test")
187f2477c2SPeter Xu #define VTD_TEST_IR_MSI ("IR MSI")
19*3223ade2SPeter Xu #define VTD_TEST_IR_IOAPIC ("IR IOAPIC")
2092d2c192SPeter Xu 
2192d2c192SPeter Xu void vtd_test_dmar(struct pci_edu_dev *dev)
2292d2c192SPeter Xu {
2392d2c192SPeter Xu 	void *page = alloc_page();
2492d2c192SPeter Xu 
259e8d01f9SPeter Xu 	report_prefix_push("vtd_dmar");
269e8d01f9SPeter Xu 
2792d2c192SPeter Xu #define DMA_TEST_WORD (0x12345678)
2892d2c192SPeter Xu 	/* Modify the first 4 bytes of the page */
2992d2c192SPeter Xu 	*(uint32_t *)page = DMA_TEST_WORD;
3092d2c192SPeter Xu 
3192d2c192SPeter Xu 	/*
3292d2c192SPeter Xu 	 * Map the newly allocated page into IOVA address 0 (size 4K)
3392d2c192SPeter Xu 	 * of the device address space. Root entry and context entry
3492d2c192SPeter Xu 	 * will be automatically created when needed.
3592d2c192SPeter Xu 	 */
3692d2c192SPeter Xu 	vtd_map_range(dev->pci_dev.bdf, 0, virt_to_phys(page), PAGE_SIZE);
3792d2c192SPeter Xu 
3892d2c192SPeter Xu 	/*
3992d2c192SPeter Xu 	 * DMA the first 4 bytes of the page to EDU device buffer
4092d2c192SPeter Xu 	 * offset 0.
4192d2c192SPeter Xu 	 */
4292d2c192SPeter Xu 	edu_dma(dev, 0, 4, 0, false);
4392d2c192SPeter Xu 
4492d2c192SPeter Xu 	/*
4592d2c192SPeter Xu 	 * DMA the first 4 bytes of EDU device buffer into the page
4692d2c192SPeter Xu 	 * with offset 4 (so it'll be using 4-7 bytes).
4792d2c192SPeter Xu 	 */
4892d2c192SPeter Xu 	edu_dma(dev, 4, 4, 0, true);
4992d2c192SPeter Xu 
5092d2c192SPeter Xu 	/*
5192d2c192SPeter Xu 	 * Check data match between 0-3 bytes and 4-7 bytes of the
5292d2c192SPeter Xu 	 * page.
5392d2c192SPeter Xu 	 */
5492d2c192SPeter Xu 	report(VTD_TEST_DMAR_4B, *((uint32_t *)page + 1) == DMA_TEST_WORD);
5592d2c192SPeter Xu 
5692d2c192SPeter Xu 	free_page(page);
579e8d01f9SPeter Xu 
589e8d01f9SPeter Xu 	report_prefix_pop();
5992d2c192SPeter Xu }
6016c0b05fSPeter Xu 
617f2477c2SPeter Xu static volatile bool edu_intr_recved;
627f2477c2SPeter Xu 
637f2477c2SPeter Xu static void edu_isr(isr_regs_t *regs)
647f2477c2SPeter Xu {
657f2477c2SPeter Xu 	edu_intr_recved = true;
667f2477c2SPeter Xu 	eoi();
677f2477c2SPeter Xu }
687f2477c2SPeter Xu 
697f2477c2SPeter Xu static void vtd_test_ir(struct pci_edu_dev *dev)
707f2477c2SPeter Xu {
71*3223ade2SPeter Xu #define VTD_TEST_VECTOR_IOAPIC (0xed)
72*3223ade2SPeter Xu #define VTD_TEST_VECTOR_MSI (0xee)
73*3223ade2SPeter Xu 	struct pci_dev *pci_dev = &dev->pci_dev;
74*3223ade2SPeter Xu 
759e8d01f9SPeter Xu 	report_prefix_push("vtd_ir");
769e8d01f9SPeter Xu 
777f2477c2SPeter Xu 	irq_enable();
787f2477c2SPeter Xu 
79*3223ade2SPeter Xu 	/* This will enable INTx */
80*3223ade2SPeter Xu 	pci_msi_set_enable(pci_dev, false);
81*3223ade2SPeter Xu 	vtd_setup_ioapic_irq(pci_dev, VTD_TEST_VECTOR_IOAPIC,
82*3223ade2SPeter Xu 			     0, TRIGGER_EDGE);
83*3223ade2SPeter Xu 	handle_irq(VTD_TEST_VECTOR_IOAPIC, edu_isr);
84*3223ade2SPeter Xu 
85*3223ade2SPeter Xu 	edu_intr_recved = false;
86*3223ade2SPeter Xu 	wmb();
877f2477c2SPeter Xu 	/* Manually trigger INTR */
887f2477c2SPeter Xu 	edu_reg_writel(dev, EDU_REG_INTR_RAISE, 1);
897f2477c2SPeter Xu 
907f2477c2SPeter Xu 	while (!edu_intr_recved)
917f2477c2SPeter Xu 		cpu_relax();
927f2477c2SPeter Xu 
937f2477c2SPeter Xu 	/* Clear INTR bits */
947f2477c2SPeter Xu 	edu_reg_writel(dev, EDU_REG_INTR_RAISE, 0);
957f2477c2SPeter Xu 
967f2477c2SPeter Xu 	/* We are good as long as we reach here */
97*3223ade2SPeter Xu 	report(VTD_TEST_IR_IOAPIC, edu_intr_recved == true);
98*3223ade2SPeter Xu 
99*3223ade2SPeter Xu 	/*
100*3223ade2SPeter Xu 	 * Setup EDU PCI device MSI, using interrupt remapping. By
101*3223ade2SPeter Xu 	 * default, EDU device is using INTx.
102*3223ade2SPeter Xu 	 */
103*3223ade2SPeter Xu 	if (!vtd_setup_msi(pci_dev, VTD_TEST_VECTOR_MSI, 0)) {
104*3223ade2SPeter Xu 		printf("edu device does not support MSI, skip test\n");
105*3223ade2SPeter Xu 		report_skip(VTD_TEST_IR_MSI);
106*3223ade2SPeter Xu 		return;
107*3223ade2SPeter Xu 	}
108*3223ade2SPeter Xu 
109*3223ade2SPeter Xu 	handle_irq(VTD_TEST_VECTOR_MSI, edu_isr);
110*3223ade2SPeter Xu 
111*3223ade2SPeter Xu 	edu_intr_recved = false;
112*3223ade2SPeter Xu 	wmb();
113*3223ade2SPeter Xu 	/* Manually trigger INTR */
114*3223ade2SPeter Xu 	edu_reg_writel(dev, EDU_REG_INTR_RAISE, 1);
115*3223ade2SPeter Xu 
116*3223ade2SPeter Xu 	while (!edu_intr_recved)
117*3223ade2SPeter Xu 		cpu_relax();
118*3223ade2SPeter Xu 
119*3223ade2SPeter Xu 	/* Clear INTR bits */
120*3223ade2SPeter Xu 	edu_reg_writel(dev, EDU_REG_INTR_RAISE, 0);
121*3223ade2SPeter Xu 
122*3223ade2SPeter Xu 	/* We are good as long as we reach here */
123*3223ade2SPeter Xu 	report(VTD_TEST_IR_MSI, edu_intr_recved == true);
1249e8d01f9SPeter Xu 
1259e8d01f9SPeter Xu 	report_prefix_pop();
1267f2477c2SPeter Xu }
1277f2477c2SPeter Xu 
12816c0b05fSPeter Xu int main(int argc, char *argv[])
12916c0b05fSPeter Xu {
13092d2c192SPeter Xu 	struct pci_edu_dev dev;
13192d2c192SPeter Xu 
13216c0b05fSPeter Xu 	vtd_init();
13316c0b05fSPeter Xu 
1349e8d01f9SPeter Xu 	report_prefix_push("vtd_init");
1359e8d01f9SPeter Xu 
13616c0b05fSPeter Xu 	report("fault status check", vtd_readl(DMAR_FSTS_REG) == 0);
13716c0b05fSPeter Xu 	report("QI enablement", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_QI);
13816c0b05fSPeter Xu 	report("DMAR table setup", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_ROOT);
13916c0b05fSPeter Xu 	report("IR table setup", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_IR_TABLE);
14016c0b05fSPeter Xu 	report("DMAR enablement", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_DMAR);
14116c0b05fSPeter Xu 	report("IR enablement", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_IR);
14292d2c192SPeter Xu 	report("DMAR support 39 bits address width",
14392d2c192SPeter Xu 	       vtd_readq(DMAR_CAP_REG) & VTD_CAP_SAGAW);
14492d2c192SPeter Xu 	report("DMAR support huge pages", vtd_readq(DMAR_CAP_REG) & VTD_CAP_SLLPS);
14592d2c192SPeter Xu 
1469e8d01f9SPeter Xu 	report_prefix_pop();
1479e8d01f9SPeter Xu 
14892d2c192SPeter Xu 	if (!edu_init(&dev)) {
14992d2c192SPeter Xu 		printf("Please specify \"-device edu\" to do "
15092d2c192SPeter Xu 		       "further IOMMU tests.\n");
15192d2c192SPeter Xu 		report_skip(VTD_TEST_DMAR_4B);
152*3223ade2SPeter Xu 		report_skip(VTD_TEST_IR_IOAPIC);
1537f2477c2SPeter Xu 		report_skip(VTD_TEST_IR_MSI);
1547f2477c2SPeter Xu 	} else {
15592d2c192SPeter Xu 		vtd_test_dmar(&dev);
1567f2477c2SPeter Xu 		vtd_test_ir(&dev);
1577f2477c2SPeter Xu 	}
15816c0b05fSPeter Xu 
15916c0b05fSPeter Xu 	return report_summary();
16016c0b05fSPeter Xu }
161