1
0

intel-iommu.c 3.9 KB

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