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" 15*7f2477c2SPeter Xu #include "x86/apic.h" 1692d2c192SPeter Xu 1792d2c192SPeter Xu #define VTD_TEST_DMAR_4B ("DMAR 4B memcpy test") 18*7f2477c2SPeter Xu #define VTD_TEST_IR_MSI ("IR MSI") 1992d2c192SPeter Xu 2092d2c192SPeter Xu void vtd_test_dmar(struct pci_edu_dev *dev) 2192d2c192SPeter Xu { 2292d2c192SPeter Xu void *page = alloc_page(); 2392d2c192SPeter Xu 2492d2c192SPeter Xu #define DMA_TEST_WORD (0x12345678) 2592d2c192SPeter Xu /* Modify the first 4 bytes of the page */ 2692d2c192SPeter Xu *(uint32_t *)page = DMA_TEST_WORD; 2792d2c192SPeter Xu 2892d2c192SPeter Xu /* 2992d2c192SPeter Xu * Map the newly allocated page into IOVA address 0 (size 4K) 3092d2c192SPeter Xu * of the device address space. Root entry and context entry 3192d2c192SPeter Xu * will be automatically created when needed. 3292d2c192SPeter Xu */ 3392d2c192SPeter Xu vtd_map_range(dev->pci_dev.bdf, 0, virt_to_phys(page), PAGE_SIZE); 3492d2c192SPeter Xu 3592d2c192SPeter Xu /* 3692d2c192SPeter Xu * DMA the first 4 bytes of the page to EDU device buffer 3792d2c192SPeter Xu * offset 0. 3892d2c192SPeter Xu */ 3992d2c192SPeter Xu edu_dma(dev, 0, 4, 0, false); 4092d2c192SPeter Xu 4192d2c192SPeter Xu /* 4292d2c192SPeter Xu * DMA the first 4 bytes of EDU device buffer into the page 4392d2c192SPeter Xu * with offset 4 (so it'll be using 4-7 bytes). 4492d2c192SPeter Xu */ 4592d2c192SPeter Xu edu_dma(dev, 4, 4, 0, true); 4692d2c192SPeter Xu 4792d2c192SPeter Xu /* 4892d2c192SPeter Xu * Check data match between 0-3 bytes and 4-7 bytes of the 4992d2c192SPeter Xu * page. 5092d2c192SPeter Xu */ 5192d2c192SPeter Xu report(VTD_TEST_DMAR_4B, *((uint32_t *)page + 1) == DMA_TEST_WORD); 5292d2c192SPeter Xu 5392d2c192SPeter Xu free_page(page); 5492d2c192SPeter Xu } 5516c0b05fSPeter Xu 56*7f2477c2SPeter Xu static volatile bool edu_intr_recved; 57*7f2477c2SPeter Xu 58*7f2477c2SPeter Xu static void edu_isr(isr_regs_t *regs) 59*7f2477c2SPeter Xu { 60*7f2477c2SPeter Xu edu_intr_recved = true; 61*7f2477c2SPeter Xu eoi(); 62*7f2477c2SPeter Xu } 63*7f2477c2SPeter Xu 64*7f2477c2SPeter Xu static void vtd_test_ir(struct pci_edu_dev *dev) 65*7f2477c2SPeter Xu { 66*7f2477c2SPeter Xu #define VTD_TEST_VECTOR (0xee) 67*7f2477c2SPeter Xu /* 68*7f2477c2SPeter Xu * Setup EDU PCI device MSI, using interrupt remapping. By 69*7f2477c2SPeter Xu * default, EDU device is using INTx. 70*7f2477c2SPeter Xu */ 71*7f2477c2SPeter Xu if (!vtd_setup_msi(&dev->pci_dev, VTD_TEST_VECTOR, 0)) { 72*7f2477c2SPeter Xu printf("edu device does not support MSI, skip test\n"); 73*7f2477c2SPeter Xu report_skip(VTD_TEST_IR_MSI); 74*7f2477c2SPeter Xu return; 75*7f2477c2SPeter Xu } 76*7f2477c2SPeter Xu 77*7f2477c2SPeter Xu handle_irq(VTD_TEST_VECTOR, edu_isr); 78*7f2477c2SPeter Xu irq_enable(); 79*7f2477c2SPeter Xu 80*7f2477c2SPeter Xu /* Manually trigger INTR */ 81*7f2477c2SPeter Xu edu_reg_writel(dev, EDU_REG_INTR_RAISE, 1); 82*7f2477c2SPeter Xu 83*7f2477c2SPeter Xu while (!edu_intr_recved) 84*7f2477c2SPeter Xu cpu_relax(); 85*7f2477c2SPeter Xu 86*7f2477c2SPeter Xu /* Clear INTR bits */ 87*7f2477c2SPeter Xu edu_reg_writel(dev, EDU_REG_INTR_RAISE, 0); 88*7f2477c2SPeter Xu 89*7f2477c2SPeter Xu /* We are good as long as we reach here */ 90*7f2477c2SPeter Xu report(VTD_TEST_IR_MSI, true); 91*7f2477c2SPeter Xu } 92*7f2477c2SPeter Xu 9316c0b05fSPeter Xu int main(int argc, char *argv[]) 9416c0b05fSPeter Xu { 9592d2c192SPeter Xu struct pci_edu_dev dev; 9692d2c192SPeter Xu 9716c0b05fSPeter Xu vtd_init(); 9816c0b05fSPeter Xu 9916c0b05fSPeter Xu report("fault status check", vtd_readl(DMAR_FSTS_REG) == 0); 10016c0b05fSPeter Xu report("QI enablement", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_QI); 10116c0b05fSPeter Xu report("DMAR table setup", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_ROOT); 10216c0b05fSPeter Xu report("IR table setup", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_IR_TABLE); 10316c0b05fSPeter Xu report("DMAR enablement", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_DMAR); 10416c0b05fSPeter Xu report("IR enablement", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_IR); 10592d2c192SPeter Xu report("DMAR support 39 bits address width", 10692d2c192SPeter Xu vtd_readq(DMAR_CAP_REG) & VTD_CAP_SAGAW); 10792d2c192SPeter Xu report("DMAR support huge pages", vtd_readq(DMAR_CAP_REG) & VTD_CAP_SLLPS); 10892d2c192SPeter Xu 10992d2c192SPeter Xu if (!edu_init(&dev)) { 11092d2c192SPeter Xu printf("Please specify \"-device edu\" to do " 11192d2c192SPeter Xu "further IOMMU tests.\n"); 11292d2c192SPeter Xu report_skip(VTD_TEST_DMAR_4B); 113*7f2477c2SPeter Xu report_skip(VTD_TEST_IR_MSI); 114*7f2477c2SPeter Xu } else { 11592d2c192SPeter Xu vtd_test_dmar(&dev); 116*7f2477c2SPeter Xu vtd_test_ir(&dev); 117*7f2477c2SPeter Xu } 11816c0b05fSPeter Xu 11916c0b05fSPeter Xu return report_summary(); 12016c0b05fSPeter Xu } 121