diff options
Diffstat (limited to 'drivers/pci/dmar.c')
-rw-r--r-- | drivers/pci/dmar.c | 303 |
1 files changed, 298 insertions, 5 deletions
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 26c536b51c5a..d313039e2fdf 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c | |||
@@ -31,6 +31,8 @@ | |||
31 | #include <linux/iova.h> | 31 | #include <linux/iova.h> |
32 | #include <linux/intel-iommu.h> | 32 | #include <linux/intel-iommu.h> |
33 | #include <linux/timer.h> | 33 | #include <linux/timer.h> |
34 | #include <linux/irq.h> | ||
35 | #include <linux/interrupt.h> | ||
34 | 36 | ||
35 | #undef PREFIX | 37 | #undef PREFIX |
36 | #define PREFIX "DMAR:" | 38 | #define PREFIX "DMAR:" |
@@ -42,6 +44,7 @@ | |||
42 | LIST_HEAD(dmar_drhd_units); | 44 | LIST_HEAD(dmar_drhd_units); |
43 | 45 | ||
44 | static struct acpi_table_header * __initdata dmar_tbl; | 46 | static struct acpi_table_header * __initdata dmar_tbl; |
47 | static acpi_size dmar_tbl_size; | ||
45 | 48 | ||
46 | static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd) | 49 | static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd) |
47 | { | 50 | { |
@@ -288,8 +291,9 @@ static int __init dmar_table_detect(void) | |||
288 | acpi_status status = AE_OK; | 291 | acpi_status status = AE_OK; |
289 | 292 | ||
290 | /* if we could find DMAR table, then there are DMAR devices */ | 293 | /* if we could find DMAR table, then there are DMAR devices */ |
291 | status = acpi_get_table(ACPI_SIG_DMAR, 0, | 294 | status = acpi_get_table_with_size(ACPI_SIG_DMAR, 0, |
292 | (struct acpi_table_header **)&dmar_tbl); | 295 | (struct acpi_table_header **)&dmar_tbl, |
296 | &dmar_tbl_size); | ||
293 | 297 | ||
294 | if (ACPI_SUCCESS(status) && !dmar_tbl) { | 298 | if (ACPI_SUCCESS(status) && !dmar_tbl) { |
295 | printk (KERN_WARNING PREFIX "Unable to map DMAR\n"); | 299 | printk (KERN_WARNING PREFIX "Unable to map DMAR\n"); |
@@ -489,6 +493,7 @@ void __init detect_intel_iommu(void) | |||
489 | iommu_detected = 1; | 493 | iommu_detected = 1; |
490 | #endif | 494 | #endif |
491 | } | 495 | } |
496 | early_acpi_os_unmap_memory(dmar_tbl, dmar_tbl_size); | ||
492 | dmar_tbl = NULL; | 497 | dmar_tbl = NULL; |
493 | } | 498 | } |
494 | 499 | ||
@@ -506,6 +511,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) | |||
506 | return -ENOMEM; | 511 | return -ENOMEM; |
507 | 512 | ||
508 | iommu->seq_id = iommu_allocated++; | 513 | iommu->seq_id = iommu_allocated++; |
514 | sprintf (iommu->name, "dmar%d", iommu->seq_id); | ||
509 | 515 | ||
510 | iommu->reg = ioremap(drhd->reg_base_addr, VTD_PAGE_SIZE); | 516 | iommu->reg = ioremap(drhd->reg_base_addr, VTD_PAGE_SIZE); |
511 | if (!iommu->reg) { | 517 | if (!iommu->reg) { |
@@ -748,6 +754,42 @@ int qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr, | |||
748 | } | 754 | } |
749 | 755 | ||
750 | /* | 756 | /* |
757 | * Disable Queued Invalidation interface. | ||
758 | */ | ||
759 | void dmar_disable_qi(struct intel_iommu *iommu) | ||
760 | { | ||
761 | unsigned long flags; | ||
762 | u32 sts; | ||
763 | cycles_t start_time = get_cycles(); | ||
764 | |||
765 | if (!ecap_qis(iommu->ecap)) | ||
766 | return; | ||
767 | |||
768 | spin_lock_irqsave(&iommu->register_lock, flags); | ||
769 | |||
770 | sts = dmar_readq(iommu->reg + DMAR_GSTS_REG); | ||
771 | if (!(sts & DMA_GSTS_QIES)) | ||
772 | goto end; | ||
773 | |||
774 | /* | ||
775 | * Give a chance to HW to complete the pending invalidation requests. | ||
776 | */ | ||
777 | while ((readl(iommu->reg + DMAR_IQT_REG) != | ||
778 | readl(iommu->reg + DMAR_IQH_REG)) && | ||
779 | (DMAR_OPERATION_TIMEOUT > (get_cycles() - start_time))) | ||
780 | cpu_relax(); | ||
781 | |||
782 | iommu->gcmd &= ~DMA_GCMD_QIE; | ||
783 | |||
784 | writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG); | ||
785 | |||
786 | IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl, | ||
787 | !(sts & DMA_GSTS_QIES), sts); | ||
788 | end: | ||
789 | spin_unlock_irqrestore(&iommu->register_lock, flags); | ||
790 | } | ||
791 | |||
792 | /* | ||
751 | * Enable Queued Invalidation interface. This is a must to support | 793 | * Enable Queued Invalidation interface. This is a must to support |
752 | * interrupt-remapping. Also used by DMA-remapping, which replaces | 794 | * interrupt-remapping. Also used by DMA-remapping, which replaces |
753 | * register based IOTLB invalidation. | 795 | * register based IOTLB invalidation. |
@@ -767,20 +809,20 @@ int dmar_enable_qi(struct intel_iommu *iommu) | |||
767 | if (iommu->qi) | 809 | if (iommu->qi) |
768 | return 0; | 810 | return 0; |
769 | 811 | ||
770 | iommu->qi = kmalloc(sizeof(*qi), GFP_KERNEL); | 812 | iommu->qi = kmalloc(sizeof(*qi), GFP_ATOMIC); |
771 | if (!iommu->qi) | 813 | if (!iommu->qi) |
772 | return -ENOMEM; | 814 | return -ENOMEM; |
773 | 815 | ||
774 | qi = iommu->qi; | 816 | qi = iommu->qi; |
775 | 817 | ||
776 | qi->desc = (void *)(get_zeroed_page(GFP_KERNEL)); | 818 | qi->desc = (void *)(get_zeroed_page(GFP_ATOMIC)); |
777 | if (!qi->desc) { | 819 | if (!qi->desc) { |
778 | kfree(qi); | 820 | kfree(qi); |
779 | iommu->qi = 0; | 821 | iommu->qi = 0; |
780 | return -ENOMEM; | 822 | return -ENOMEM; |
781 | } | 823 | } |
782 | 824 | ||
783 | qi->desc_status = kmalloc(QI_LENGTH * sizeof(int), GFP_KERNEL); | 825 | qi->desc_status = kmalloc(QI_LENGTH * sizeof(int), GFP_ATOMIC); |
784 | if (!qi->desc_status) { | 826 | if (!qi->desc_status) { |
785 | free_page((unsigned long) qi->desc); | 827 | free_page((unsigned long) qi->desc); |
786 | kfree(qi); | 828 | kfree(qi); |
@@ -809,3 +851,254 @@ int dmar_enable_qi(struct intel_iommu *iommu) | |||
809 | 851 | ||
810 | return 0; | 852 | return 0; |
811 | } | 853 | } |
854 | |||
855 | /* iommu interrupt handling. Most stuff are MSI-like. */ | ||
856 | |||
857 | enum faulttype { | ||
858 | DMA_REMAP, | ||
859 | INTR_REMAP, | ||
860 | UNKNOWN, | ||
861 | }; | ||
862 | |||
863 | static const char *dma_remap_fault_reasons[] = | ||
864 | { | ||
865 | "Software", | ||
866 | "Present bit in root entry is clear", | ||
867 | "Present bit in context entry is clear", | ||
868 | "Invalid context entry", | ||
869 | "Access beyond MGAW", | ||
870 | "PTE Write access is not set", | ||
871 | "PTE Read access is not set", | ||
872 | "Next page table ptr is invalid", | ||
873 | "Root table address invalid", | ||
874 | "Context table ptr is invalid", | ||
875 | "non-zero reserved fields in RTP", | ||
876 | "non-zero reserved fields in CTP", | ||
877 | "non-zero reserved fields in PTE", | ||
878 | }; | ||
879 | |||
880 | static const char *intr_remap_fault_reasons[] = | ||
881 | { | ||
882 | "Detected reserved fields in the decoded interrupt-remapped request", | ||
883 | "Interrupt index exceeded the interrupt-remapping table size", | ||
884 | "Present field in the IRTE entry is clear", | ||
885 | "Error accessing interrupt-remapping table pointed by IRTA_REG", | ||
886 | "Detected reserved fields in the IRTE entry", | ||
887 | "Blocked a compatibility format interrupt request", | ||
888 | "Blocked an interrupt request due to source-id verification failure", | ||
889 | }; | ||
890 | |||
891 | #define MAX_FAULT_REASON_IDX (ARRAY_SIZE(fault_reason_strings) - 1) | ||
892 | |||
893 | const char *dmar_get_fault_reason(u8 fault_reason, int *fault_type) | ||
894 | { | ||
895 | if (fault_reason >= 0x20 && (fault_reason <= 0x20 + | ||
896 | ARRAY_SIZE(intr_remap_fault_reasons))) { | ||
897 | *fault_type = INTR_REMAP; | ||
898 | return intr_remap_fault_reasons[fault_reason - 0x20]; | ||
899 | } else if (fault_reason < ARRAY_SIZE(dma_remap_fault_reasons)) { | ||
900 | *fault_type = DMA_REMAP; | ||
901 | return dma_remap_fault_reasons[fault_reason]; | ||
902 | } else { | ||
903 | *fault_type = UNKNOWN; | ||
904 | return "Unknown"; | ||
905 | } | ||
906 | } | ||
907 | |||
908 | void dmar_msi_unmask(unsigned int irq) | ||
909 | { | ||
910 | struct intel_iommu *iommu = get_irq_data(irq); | ||
911 | unsigned long flag; | ||
912 | |||
913 | /* unmask it */ | ||
914 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
915 | writel(0, iommu->reg + DMAR_FECTL_REG); | ||
916 | /* Read a reg to force flush the post write */ | ||
917 | readl(iommu->reg + DMAR_FECTL_REG); | ||
918 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
919 | } | ||
920 | |||
921 | void dmar_msi_mask(unsigned int irq) | ||
922 | { | ||
923 | unsigned long flag; | ||
924 | struct intel_iommu *iommu = get_irq_data(irq); | ||
925 | |||
926 | /* mask it */ | ||
927 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
928 | writel(DMA_FECTL_IM, iommu->reg + DMAR_FECTL_REG); | ||
929 | /* Read a reg to force flush the post write */ | ||
930 | readl(iommu->reg + DMAR_FECTL_REG); | ||
931 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
932 | } | ||
933 | |||
934 | void dmar_msi_write(int irq, struct msi_msg *msg) | ||
935 | { | ||
936 | struct intel_iommu *iommu = get_irq_data(irq); | ||
937 | unsigned long flag; | ||
938 | |||
939 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
940 | writel(msg->data, iommu->reg + DMAR_FEDATA_REG); | ||
941 | writel(msg->address_lo, iommu->reg + DMAR_FEADDR_REG); | ||
942 | writel(msg->address_hi, iommu->reg + DMAR_FEUADDR_REG); | ||
943 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
944 | } | ||
945 | |||
946 | void dmar_msi_read(int irq, struct msi_msg *msg) | ||
947 | { | ||
948 | struct intel_iommu *iommu = get_irq_data(irq); | ||
949 | unsigned long flag; | ||
950 | |||
951 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
952 | msg->data = readl(iommu->reg + DMAR_FEDATA_REG); | ||
953 | msg->address_lo = readl(iommu->reg + DMAR_FEADDR_REG); | ||
954 | msg->address_hi = readl(iommu->reg + DMAR_FEUADDR_REG); | ||
955 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
956 | } | ||
957 | |||
958 | static int dmar_fault_do_one(struct intel_iommu *iommu, int type, | ||
959 | u8 fault_reason, u16 source_id, unsigned long long addr) | ||
960 | { | ||
961 | const char *reason; | ||
962 | int fault_type; | ||
963 | |||
964 | reason = dmar_get_fault_reason(fault_reason, &fault_type); | ||
965 | |||
966 | if (fault_type == INTR_REMAP) | ||
967 | printk(KERN_ERR "INTR-REMAP: Request device [[%02x:%02x.%d] " | ||
968 | "fault index %llx\n" | ||
969 | "INTR-REMAP:[fault reason %02d] %s\n", | ||
970 | (source_id >> 8), PCI_SLOT(source_id & 0xFF), | ||
971 | PCI_FUNC(source_id & 0xFF), addr >> 48, | ||
972 | fault_reason, reason); | ||
973 | else | ||
974 | printk(KERN_ERR | ||
975 | "DMAR:[%s] Request device [%02x:%02x.%d] " | ||
976 | "fault addr %llx \n" | ||
977 | "DMAR:[fault reason %02d] %s\n", | ||
978 | (type ? "DMA Read" : "DMA Write"), | ||
979 | (source_id >> 8), PCI_SLOT(source_id & 0xFF), | ||
980 | PCI_FUNC(source_id & 0xFF), addr, fault_reason, reason); | ||
981 | return 0; | ||
982 | } | ||
983 | |||
984 | #define PRIMARY_FAULT_REG_LEN (16) | ||
985 | irqreturn_t dmar_fault(int irq, void *dev_id) | ||
986 | { | ||
987 | struct intel_iommu *iommu = dev_id; | ||
988 | int reg, fault_index; | ||
989 | u32 fault_status; | ||
990 | unsigned long flag; | ||
991 | |||
992 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
993 | fault_status = readl(iommu->reg + DMAR_FSTS_REG); | ||
994 | if (fault_status) | ||
995 | printk(KERN_ERR "DRHD: handling fault status reg %x\n", | ||
996 | fault_status); | ||
997 | |||
998 | /* TBD: ignore advanced fault log currently */ | ||
999 | if (!(fault_status & DMA_FSTS_PPF)) | ||
1000 | goto clear_rest; | ||
1001 | |||
1002 | fault_index = dma_fsts_fault_record_index(fault_status); | ||
1003 | reg = cap_fault_reg_offset(iommu->cap); | ||
1004 | while (1) { | ||
1005 | u8 fault_reason; | ||
1006 | u16 source_id; | ||
1007 | u64 guest_addr; | ||
1008 | int type; | ||
1009 | u32 data; | ||
1010 | |||
1011 | /* highest 32 bits */ | ||
1012 | data = readl(iommu->reg + reg + | ||
1013 | fault_index * PRIMARY_FAULT_REG_LEN + 12); | ||
1014 | if (!(data & DMA_FRCD_F)) | ||
1015 | break; | ||
1016 | |||
1017 | fault_reason = dma_frcd_fault_reason(data); | ||
1018 | type = dma_frcd_type(data); | ||
1019 | |||
1020 | data = readl(iommu->reg + reg + | ||
1021 | fault_index * PRIMARY_FAULT_REG_LEN + 8); | ||
1022 | source_id = dma_frcd_source_id(data); | ||
1023 | |||
1024 | guest_addr = dmar_readq(iommu->reg + reg + | ||
1025 | fault_index * PRIMARY_FAULT_REG_LEN); | ||
1026 | guest_addr = dma_frcd_page_addr(guest_addr); | ||
1027 | /* clear the fault */ | ||
1028 | writel(DMA_FRCD_F, iommu->reg + reg + | ||
1029 | fault_index * PRIMARY_FAULT_REG_LEN + 12); | ||
1030 | |||
1031 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
1032 | |||
1033 | dmar_fault_do_one(iommu, type, fault_reason, | ||
1034 | source_id, guest_addr); | ||
1035 | |||
1036 | fault_index++; | ||
1037 | if (fault_index > cap_num_fault_regs(iommu->cap)) | ||
1038 | fault_index = 0; | ||
1039 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
1040 | } | ||
1041 | clear_rest: | ||
1042 | /* clear all the other faults */ | ||
1043 | fault_status = readl(iommu->reg + DMAR_FSTS_REG); | ||
1044 | writel(fault_status, iommu->reg + DMAR_FSTS_REG); | ||
1045 | |||
1046 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
1047 | return IRQ_HANDLED; | ||
1048 | } | ||
1049 | |||
1050 | int dmar_set_interrupt(struct intel_iommu *iommu) | ||
1051 | { | ||
1052 | int irq, ret; | ||
1053 | |||
1054 | /* | ||
1055 | * Check if the fault interrupt is already initialized. | ||
1056 | */ | ||
1057 | if (iommu->irq) | ||
1058 | return 0; | ||
1059 | |||
1060 | irq = create_irq(); | ||
1061 | if (!irq) { | ||
1062 | printk(KERN_ERR "IOMMU: no free vectors\n"); | ||
1063 | return -EINVAL; | ||
1064 | } | ||
1065 | |||
1066 | set_irq_data(irq, iommu); | ||
1067 | iommu->irq = irq; | ||
1068 | |||
1069 | ret = arch_setup_dmar_msi(irq); | ||
1070 | if (ret) { | ||
1071 | set_irq_data(irq, NULL); | ||
1072 | iommu->irq = 0; | ||
1073 | destroy_irq(irq); | ||
1074 | return 0; | ||
1075 | } | ||
1076 | |||
1077 | ret = request_irq(irq, dmar_fault, 0, iommu->name, iommu); | ||
1078 | if (ret) | ||
1079 | printk(KERN_ERR "IOMMU: can't request irq\n"); | ||
1080 | return ret; | ||
1081 | } | ||
1082 | |||
1083 | int __init enable_drhd_fault_handling(void) | ||
1084 | { | ||
1085 | struct dmar_drhd_unit *drhd; | ||
1086 | |||
1087 | /* | ||
1088 | * Enable fault control interrupt. | ||
1089 | */ | ||
1090 | for_each_drhd_unit(drhd) { | ||
1091 | int ret; | ||
1092 | struct intel_iommu *iommu = drhd->iommu; | ||
1093 | ret = dmar_set_interrupt(iommu); | ||
1094 | |||
1095 | if (ret) { | ||
1096 | printk(KERN_ERR "DRHD %Lx: failed to enable fault, " | ||
1097 | " interrupt, ret %d\n", | ||
1098 | (unsigned long long)drhd->reg_base_addr, ret); | ||
1099 | return -1; | ||
1100 | } | ||
1101 | } | ||
1102 | |||
1103 | return 0; | ||
1104 | } | ||