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 #define VTD_TEST_IR_IOAPIC ("IR IOAPIC") 20 21 static struct pci_edu_dev edu_dev; 22 23 static void vtd_test_dmar(void) 24 { 25 struct pci_edu_dev *dev = &edu_dev; 26 void *page = alloc_page(); 27 28 report_prefix_push("vtd_dmar"); 29 30 #define DMA_TEST_WORD (0x12345678) 31 /* Modify the first 4 bytes of the page */ 32 *(uint32_t *)page = DMA_TEST_WORD; 33 34 /* 35 * Map the newly allocated page into IOVA address 0 (size 4K) 36 * of the device address space. Root entry and context entry 37 * will be automatically created when needed. 38 */ 39 vtd_map_range(dev->pci_dev.bdf, 0, virt_to_phys(page), PAGE_SIZE); 40 41 /* 42 * DMA the first 4 bytes of the page to EDU device buffer 43 * offset 0. 44 */ 45 edu_dma(dev, 0, 4, 0, false); 46 47 /* 48 * DMA the first 4 bytes of EDU device buffer into the page 49 * with offset 4 (so it'll be using 4-7 bytes). 50 */ 51 edu_dma(dev, 4, 4, 0, true); 52 53 /* 54 * Check data match between 0-3 bytes and 4-7 bytes of the 55 * page. 56 */ 57 report(VTD_TEST_DMAR_4B, *((uint32_t *)page + 1) == DMA_TEST_WORD); 58 59 free_page(page); 60 61 report_prefix_pop(); 62 } 63 64 static volatile bool edu_intr_recved; 65 66 static void edu_isr(isr_regs_t *regs) 67 { 68 edu_intr_recved = true; 69 eoi(); 70 edu_reg_writel(&edu_dev, EDU_REG_INTR_ACK, 71 edu_reg_readl(&edu_dev, EDU_REG_INTR_STATUS)); 72 } 73 74 static void vtd_test_ir(void) 75 { 76 #define VTD_TEST_VECTOR_IOAPIC (0xed) 77 #define VTD_TEST_VECTOR_MSI (0xee) 78 struct pci_edu_dev *dev = &edu_dev; 79 struct pci_dev *pci_dev = &dev->pci_dev; 80 81 report_prefix_push("vtd_ir"); 82 83 irq_enable(); 84 85 /* This will enable INTx */ 86 pci_msi_set_enable(pci_dev, false); 87 vtd_setup_ioapic_irq(pci_dev, VTD_TEST_VECTOR_IOAPIC, 88 0, TRIGGER_EDGE); 89 handle_irq(VTD_TEST_VECTOR_IOAPIC, edu_isr); 90 91 edu_intr_recved = false; 92 wmb(); 93 /* Manually trigger INTR */ 94 edu_reg_writel(dev, EDU_REG_INTR_RAISE, 1); 95 96 while (!edu_intr_recved) 97 cpu_relax(); 98 99 /* Clear INTR bits */ 100 edu_reg_writel(dev, EDU_REG_INTR_RAISE, 0); 101 102 /* We are good as long as we reach here */ 103 report(VTD_TEST_IR_IOAPIC, edu_intr_recved == true); 104 105 /* 106 * Setup EDU PCI device MSI, using interrupt remapping. By 107 * default, EDU device is using INTx. 108 */ 109 if (!vtd_setup_msi(pci_dev, VTD_TEST_VECTOR_MSI, 0)) { 110 printf("edu device does not support MSI, skip test\n"); 111 report_skip(VTD_TEST_IR_MSI); 112 return; 113 } 114 115 handle_irq(VTD_TEST_VECTOR_MSI, edu_isr); 116 117 edu_intr_recved = false; 118 wmb(); 119 /* Manually trigger INTR */ 120 edu_reg_writel(dev, EDU_REG_INTR_RAISE, 1); 121 122 while (!edu_intr_recved) 123 cpu_relax(); 124 125 /* We are good as long as we reach here */ 126 report(VTD_TEST_IR_MSI, edu_intr_recved == true); 127 128 report_prefix_pop(); 129 } 130 131 int main(int argc, char *argv[]) 132 { 133 vtd_init(); 134 135 report_prefix_push("vtd_init"); 136 137 report("fault status check", vtd_readl(DMAR_FSTS_REG) == 0); 138 report("QI enablement", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_QI); 139 report("DMAR table setup", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_ROOT); 140 report("IR table setup", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_IR_TABLE); 141 report("DMAR enablement", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_DMAR); 142 report("IR enablement", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_IR); 143 report("DMAR support 39 bits address width", 144 vtd_readq(DMAR_CAP_REG) & VTD_CAP_SAGAW); 145 report("DMAR support huge pages", vtd_readq(DMAR_CAP_REG) & VTD_CAP_SLLPS); 146 147 report_prefix_pop(); 148 149 if (!edu_init(&edu_dev)) { 150 printf("Please specify \"-device edu\" to do " 151 "further IOMMU tests.\n"); 152 report_skip(VTD_TEST_DMAR_4B); 153 report_skip(VTD_TEST_IR_IOAPIC); 154 report_skip(VTD_TEST_IR_MSI); 155 } else { 156 printf("Found EDU device:\n"); 157 pci_dev_print(edu_dev.pci_dev.bdf); 158 vtd_test_dmar(); 159 vtd_test_ir(); 160 } 161 162 return report_summary(); 163 } 164