| /* |
| * Intel IOMMU unit test. |
| * |
| * Copyright (C) 2016 Red Hat, Inc. |
| * |
| * Authors: |
| * Peter Xu <peterx@redhat.com>, |
| * |
| * This work is licensed under the terms of the GNU LGPL, version 2 or |
| * later. |
| */ |
| |
| #include "intel-iommu.h" |
| #include "pci-edu.h" |
| #include "x86/apic.h" |
| #include "vm.h" |
| #include "alloc_page.h" |
| |
| #define VTD_TEST_DMAR_4B ("DMAR 4B memcpy test") |
| #define VTD_TEST_IR_MSI ("IR MSI") |
| #define VTD_TEST_IR_IOAPIC ("IR IOAPIC") |
| |
| static struct pci_edu_dev edu_dev; |
| |
| static void vtd_test_dmar(void) |
| { |
| struct pci_edu_dev *dev = &edu_dev; |
| void *page = alloc_page(); |
| |
| report_prefix_push("vtd_dmar"); |
| |
| #define DMA_TEST_WORD (0x12345678) |
| /* Modify the first 4 bytes of the page */ |
| *(uint32_t *)page = DMA_TEST_WORD; |
| |
| /* |
| * Map the newly allocated page into IOVA address 0 (size 4K) |
| * of the device address space. Root entry and context entry |
| * will be automatically created when needed. |
| */ |
| vtd_map_range(dev->pci_dev.bdf, 0, virt_to_phys(page), PAGE_SIZE); |
| |
| /* |
| * DMA the first 4 bytes of the page to EDU device buffer |
| * offset 0. |
| */ |
| edu_dma(dev, 0, 4, 0, false); |
| |
| /* |
| * DMA the first 4 bytes of EDU device buffer into the page |
| * with offset 4 (so it'll be using 4-7 bytes). |
| */ |
| edu_dma(dev, 4, 4, 0, true); |
| |
| /* |
| * Check data match between 0-3 bytes and 4-7 bytes of the |
| * page. |
| */ |
| report(*((uint32_t *)page + 1) == DMA_TEST_WORD, VTD_TEST_DMAR_4B); |
| |
| free_page(page); |
| |
| report_prefix_pop(); |
| } |
| |
| static volatile bool edu_intr_recved; |
| |
| static void edu_isr(isr_regs_t *regs) |
| { |
| edu_intr_recved = true; |
| eoi(); |
| edu_reg_writel(&edu_dev, EDU_REG_INTR_ACK, |
| edu_reg_readl(&edu_dev, EDU_REG_INTR_STATUS)); |
| } |
| |
| static void vtd_test_ir(void) |
| { |
| #define VTD_TEST_VECTOR_IOAPIC (0xed) |
| #define VTD_TEST_VECTOR_MSI (0xee) |
| struct pci_edu_dev *dev = &edu_dev; |
| struct pci_dev *pci_dev = &dev->pci_dev; |
| |
| report_prefix_push("vtd_ir"); |
| |
| sti(); |
| |
| /* This will enable INTx */ |
| pci_msi_set_enable(pci_dev, false); |
| vtd_setup_ioapic_irq(pci_dev, VTD_TEST_VECTOR_IOAPIC, |
| 0, TRIGGER_EDGE); |
| handle_irq(VTD_TEST_VECTOR_IOAPIC, edu_isr); |
| |
| edu_intr_recved = false; |
| wmb(); |
| /* Manually trigger INTR */ |
| edu_reg_writel(dev, EDU_REG_INTR_RAISE, 1); |
| |
| while (!edu_intr_recved) |
| cpu_relax(); |
| |
| /* Clear INTR bits */ |
| edu_reg_writel(dev, EDU_REG_INTR_RAISE, 0); |
| |
| /* We are good as long as we reach here */ |
| report(edu_intr_recved == true, VTD_TEST_IR_IOAPIC); |
| |
| /* |
| * Setup EDU PCI device MSI, using interrupt remapping. By |
| * default, EDU device is using INTx. |
| */ |
| if (!vtd_setup_msi(pci_dev, VTD_TEST_VECTOR_MSI, 0)) { |
| printf("edu device does not support MSI, skip test\n"); |
| report_skip(VTD_TEST_IR_MSI); |
| return; |
| } |
| |
| handle_irq(VTD_TEST_VECTOR_MSI, edu_isr); |
| |
| edu_intr_recved = false; |
| wmb(); |
| /* Manually trigger INTR */ |
| edu_reg_writel(dev, EDU_REG_INTR_RAISE, 1); |
| |
| while (!edu_intr_recved) |
| cpu_relax(); |
| |
| /* We are good as long as we reach here */ |
| report(edu_intr_recved == true, VTD_TEST_IR_MSI); |
| |
| report_prefix_pop(); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| setup_vm(); |
| |
| vtd_init(); |
| |
| report_prefix_push("vtd_init"); |
| |
| report(vtd_readl(DMAR_FSTS_REG) == 0, "fault status check"); |
| report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_QI, "QI enablement"); |
| report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_ROOT, "DMAR table setup"); |
| report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_IR_TABLE, "IR table setup"); |
| report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_DMAR, "DMAR enablement"); |
| report(vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_IR, "IR enablement"); |
| report(vtd_readq(DMAR_CAP_REG) & VTD_CAP_SAGAW, |
| "DMAR support 39 bits address width"); |
| report(vtd_readq(DMAR_CAP_REG) & VTD_CAP_SLLPS, |
| "DMAR support huge pages"); |
| |
| report_prefix_pop(); |
| |
| if (!edu_init(&edu_dev)) { |
| printf("Please specify \"-device edu\" to do " |
| "further IOMMU tests.\n"); |
| report_skip(VTD_TEST_DMAR_4B); |
| report_skip(VTD_TEST_IR_IOAPIC); |
| report_skip(VTD_TEST_IR_MSI); |
| } else { |
| printf("Found EDU device:\n"); |
| pci_dev_print(&edu_dev.pci_dev); |
| vtd_test_dmar(); |
| vtd_test_ir(); |
| } |
| |
| return report_summary(); |
| } |