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"
165aca024eSPaolo Bonzini #include "vm.h"
175aca024eSPaolo Bonzini #include "alloc_page.h"
1892d2c192SPeter Xu
1992d2c192SPeter Xu #define VTD_TEST_DMAR_4B ("DMAR 4B memcpy test")
207f2477c2SPeter Xu #define VTD_TEST_IR_MSI ("IR MSI")
213223ade2SPeter Xu #define VTD_TEST_IR_IOAPIC ("IR IOAPIC")
2292d2c192SPeter Xu
23c15c99f8SAndrew Jones static struct pci_edu_dev edu_dev;
24c15c99f8SAndrew Jones
vtd_test_dmar(void)25c15c99f8SAndrew Jones static void vtd_test_dmar(void)
2692d2c192SPeter Xu {
27c15c99f8SAndrew Jones struct pci_edu_dev *dev = &edu_dev;
2892d2c192SPeter Xu void *page = alloc_page();
2992d2c192SPeter Xu
309e8d01f9SPeter Xu report_prefix_push("vtd_dmar");
319e8d01f9SPeter Xu
3292d2c192SPeter Xu #define DMA_TEST_WORD (0x12345678)
3392d2c192SPeter Xu /* Modify the first 4 bytes of the page */
3492d2c192SPeter Xu *(uint32_t *)page = DMA_TEST_WORD;
3592d2c192SPeter Xu
3692d2c192SPeter Xu /*
3792d2c192SPeter Xu * Map the newly allocated page into IOVA address 0 (size 4K)
3892d2c192SPeter Xu * of the device address space. Root entry and context entry
3992d2c192SPeter Xu * will be automatically created when needed.
4092d2c192SPeter Xu */
4192d2c192SPeter Xu vtd_map_range(dev->pci_dev.bdf, 0, virt_to_phys(page), PAGE_SIZE);
4292d2c192SPeter Xu
4392d2c192SPeter Xu /*
4492d2c192SPeter Xu * DMA the first 4 bytes of the page to EDU device buffer
4592d2c192SPeter Xu * offset 0.
4692d2c192SPeter Xu */
4792d2c192SPeter Xu edu_dma(dev, 0, 4, 0, false);
4892d2c192SPeter Xu
4992d2c192SPeter Xu /*
5092d2c192SPeter Xu * DMA the first 4 bytes of EDU device buffer into the page
5192d2c192SPeter Xu * with offset 4 (so it'll be using 4-7 bytes).
5292d2c192SPeter Xu */
5392d2c192SPeter Xu edu_dma(dev, 4, 4, 0, true);
5492d2c192SPeter Xu
5592d2c192SPeter Xu /*
5692d2c192SPeter Xu * Check data match between 0-3 bytes and 4-7 bytes of the
5792d2c192SPeter Xu * page.
5892d2c192SPeter Xu */
59a299895bSThomas Huth report(*((uint32_t *)page + 1) == DMA_TEST_WORD, VTD_TEST_DMAR_4B);
6092d2c192SPeter Xu
6192d2c192SPeter Xu free_page(page);
629e8d01f9SPeter Xu
639e8d01f9SPeter Xu report_prefix_pop();
6492d2c192SPeter Xu }
6516c0b05fSPeter Xu
667f2477c2SPeter Xu static volatile bool edu_intr_recved;
677f2477c2SPeter Xu
edu_isr(isr_regs_t * regs)687f2477c2SPeter Xu static void edu_isr(isr_regs_t *regs)
697f2477c2SPeter Xu {
707f2477c2SPeter Xu edu_intr_recved = true;
717f2477c2SPeter Xu eoi();
728446858dSAndrew Jones edu_reg_writel(&edu_dev, EDU_REG_INTR_ACK,
738446858dSAndrew Jones edu_reg_readl(&edu_dev, EDU_REG_INTR_STATUS));
747f2477c2SPeter Xu }
757f2477c2SPeter Xu
vtd_test_ir(void)76c15c99f8SAndrew Jones static void vtd_test_ir(void)
777f2477c2SPeter Xu {
783223ade2SPeter Xu #define VTD_TEST_VECTOR_IOAPIC (0xed)
793223ade2SPeter Xu #define VTD_TEST_VECTOR_MSI (0xee)
80c15c99f8SAndrew Jones struct pci_edu_dev *dev = &edu_dev;
813223ade2SPeter Xu struct pci_dev *pci_dev = &dev->pci_dev;
823223ade2SPeter Xu
839e8d01f9SPeter Xu report_prefix_push("vtd_ir");
849e8d01f9SPeter Xu
85*787f0aebSMaxim Levitsky sti();
867f2477c2SPeter Xu
873223ade2SPeter Xu /* This will enable INTx */
883223ade2SPeter Xu pci_msi_set_enable(pci_dev, false);
893223ade2SPeter Xu vtd_setup_ioapic_irq(pci_dev, VTD_TEST_VECTOR_IOAPIC,
903223ade2SPeter Xu 0, TRIGGER_EDGE);
913223ade2SPeter Xu handle_irq(VTD_TEST_VECTOR_IOAPIC, edu_isr);
923223ade2SPeter Xu
933223ade2SPeter Xu edu_intr_recved = false;
943223ade2SPeter Xu wmb();
957f2477c2SPeter Xu /* Manually trigger INTR */
967f2477c2SPeter Xu edu_reg_writel(dev, EDU_REG_INTR_RAISE, 1);
977f2477c2SPeter Xu
987f2477c2SPeter Xu while (!edu_intr_recved)
997f2477c2SPeter Xu cpu_relax();
1007f2477c2SPeter Xu
1017f2477c2SPeter Xu /* Clear INTR bits */
1027f2477c2SPeter Xu edu_reg_writel(dev, EDU_REG_INTR_RAISE, 0);
1037f2477c2SPeter Xu
1047f2477c2SPeter Xu /* We are good as long as we reach here */
105a299895bSThomas Huth report(edu_intr_recved == true, VTD_TEST_IR_IOAPIC);
1063223ade2SPeter Xu
1073223ade2SPeter Xu /*
1083223ade2SPeter Xu * Setup EDU PCI device MSI, using interrupt remapping. By
1093223ade2SPeter Xu * default, EDU device is using INTx.
1103223ade2SPeter Xu */
1113223ade2SPeter Xu if (!vtd_setup_msi(pci_dev, VTD_TEST_VECTOR_MSI, 0)) {
1123223ade2SPeter Xu printf("edu device does not support MSI, skip test\n");
1133223ade2SPeter Xu report_skip(VTD_TEST_IR_MSI);
1143223ade2SPeter Xu return;
1153223ade2SPeter Xu }
1163223ade2SPeter Xu
1173223ade2SPeter Xu handle_irq(VTD_TEST_VECTOR_MSI, edu_isr);
1183223ade2SPeter Xu
1193223ade2SPeter Xu edu_intr_recved = false;
1203223ade2SPeter Xu wmb();
1213223ade2SPeter Xu /* Manually trigger INTR */
1223223ade2SPeter Xu edu_reg_writel(dev, EDU_REG_INTR_RAISE, 1);
1233223ade2SPeter Xu
1243223ade2SPeter Xu while (!edu_intr_recved)
1253223ade2SPeter Xu cpu_relax();
1263223ade2SPeter Xu
1273223ade2SPeter Xu /* We are good as long as we reach here */
128a299895bSThomas Huth report(edu_intr_recved == true, VTD_TEST_IR_MSI);
1299e8d01f9SPeter Xu
1309e8d01f9SPeter Xu report_prefix_pop();
1317f2477c2SPeter Xu }
1327f2477c2SPeter Xu
main(int argc,char * argv[])13316c0b05fSPeter Xu int main(int argc, char *argv[])
13416c0b05fSPeter Xu {
13587194800SPaolo Bonzini setup_vm();
13687194800SPaolo Bonzini
13716c0b05fSPeter Xu vtd_init();
13816c0b05fSPeter Xu
1399e8d01f9SPeter Xu report_prefix_push("vtd_init");
1409e8d01f9SPeter Xu
141a299895bSThomas Huth report(vtd_readl(DMAR_FSTS_REG) == 0, "fault status check");
142a299895bSThomas Huth report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_QI, "QI enablement");
143a299895bSThomas Huth report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_ROOT, "DMAR table setup");
144a299895bSThomas Huth report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_IR_TABLE, "IR table setup");
145a299895bSThomas Huth report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_DMAR, "DMAR enablement");
146a299895bSThomas Huth report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_IR, "IR enablement");
147a299895bSThomas Huth report(vtd_readq(DMAR_CAP_REG) & VTD_CAP_SAGAW,
148a299895bSThomas Huth "DMAR support 39 bits address width");
149a299895bSThomas Huth report(vtd_readq(DMAR_CAP_REG) & VTD_CAP_SLLPS,
150a299895bSThomas Huth "DMAR support huge pages");
15192d2c192SPeter Xu
1529e8d01f9SPeter Xu report_prefix_pop();
1539e8d01f9SPeter Xu
154c15c99f8SAndrew Jones if (!edu_init(&edu_dev)) {
15592d2c192SPeter Xu printf("Please specify \"-device edu\" to do "
15692d2c192SPeter Xu "further IOMMU tests.\n");
15792d2c192SPeter Xu report_skip(VTD_TEST_DMAR_4B);
1583223ade2SPeter Xu report_skip(VTD_TEST_IR_IOAPIC);
1597f2477c2SPeter Xu report_skip(VTD_TEST_IR_MSI);
1607f2477c2SPeter Xu } else {
16182f2f21aSAndrew Jones printf("Found EDU device:\n");
162cb026028SAlexander Gordeev pci_dev_print(&edu_dev.pci_dev);
163c15c99f8SAndrew Jones vtd_test_dmar();
164c15c99f8SAndrew Jones vtd_test_ir();
1657f2477c2SPeter Xu }
16616c0b05fSPeter Xu
16716c0b05fSPeter Xu return report_summary();
16816c0b05fSPeter Xu }
169