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