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 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 */ 59*a299895bSThomas 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 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 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 857f2477c2SPeter Xu irq_enable(); 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 */ 105*a299895bSThomas 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 */ 128*a299895bSThomas Huth report(edu_intr_recved == true, VTD_TEST_IR_MSI); 1299e8d01f9SPeter Xu 1309e8d01f9SPeter Xu report_prefix_pop(); 1317f2477c2SPeter Xu } 1327f2477c2SPeter Xu 13316c0b05fSPeter Xu int main(int argc, char *argv[]) 13416c0b05fSPeter Xu { 13587194800SPaolo Bonzini setup_vm(); 13687194800SPaolo Bonzini smp_init(); 13787194800SPaolo Bonzini 13816c0b05fSPeter Xu vtd_init(); 13916c0b05fSPeter Xu 1409e8d01f9SPeter Xu report_prefix_push("vtd_init"); 1419e8d01f9SPeter Xu 142*a299895bSThomas Huth report(vtd_readl(DMAR_FSTS_REG) == 0, "fault status check"); 143*a299895bSThomas Huth report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_QI, "QI enablement"); 144*a299895bSThomas Huth report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_ROOT, "DMAR table setup"); 145*a299895bSThomas Huth report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_IR_TABLE, "IR table setup"); 146*a299895bSThomas Huth report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_DMAR, "DMAR enablement"); 147*a299895bSThomas Huth report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_IR, "IR enablement"); 148*a299895bSThomas Huth report(vtd_readq(DMAR_CAP_REG) & VTD_CAP_SAGAW, 149*a299895bSThomas Huth "DMAR support 39 bits address width"); 150*a299895bSThomas Huth report(vtd_readq(DMAR_CAP_REG) & VTD_CAP_SLLPS, 151*a299895bSThomas Huth "DMAR support huge pages"); 15292d2c192SPeter Xu 1539e8d01f9SPeter Xu report_prefix_pop(); 1549e8d01f9SPeter Xu 155c15c99f8SAndrew Jones if (!edu_init(&edu_dev)) { 15692d2c192SPeter Xu printf("Please specify \"-device edu\" to do " 15792d2c192SPeter Xu "further IOMMU tests.\n"); 15892d2c192SPeter Xu report_skip(VTD_TEST_DMAR_4B); 1593223ade2SPeter Xu report_skip(VTD_TEST_IR_IOAPIC); 1607f2477c2SPeter Xu report_skip(VTD_TEST_IR_MSI); 1617f2477c2SPeter Xu } else { 16282f2f21aSAndrew Jones printf("Found EDU device:\n"); 163cb026028SAlexander Gordeev pci_dev_print(&edu_dev.pci_dev); 164c15c99f8SAndrew Jones vtd_test_dmar(); 165c15c99f8SAndrew Jones vtd_test_ir(); 1667f2477c2SPeter Xu } 16716c0b05fSPeter Xu 16816c0b05fSPeter Xu return report_summary(); 16916c0b05fSPeter Xu } 170