diff options
Diffstat (limited to 'drivers/pci/dmar.c')
-rw-r--r-- | drivers/pci/dmar.c | 102 |
1 files changed, 84 insertions, 18 deletions
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 75d34bf2db5..bb4ed985f9c 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c | |||
@@ -511,6 +511,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) | |||
511 | return -ENOMEM; | 511 | return -ENOMEM; |
512 | 512 | ||
513 | iommu->seq_id = iommu_allocated++; | 513 | iommu->seq_id = iommu_allocated++; |
514 | sprintf (iommu->name, "dmar%d", iommu->seq_id); | ||
514 | 515 | ||
515 | iommu->reg = ioremap(drhd->reg_base_addr, VTD_PAGE_SIZE); | 516 | iommu->reg = ioremap(drhd->reg_base_addr, VTD_PAGE_SIZE); |
516 | if (!iommu->reg) { | 517 | if (!iommu->reg) { |
@@ -817,7 +818,13 @@ int dmar_enable_qi(struct intel_iommu *iommu) | |||
817 | 818 | ||
818 | /* iommu interrupt handling. Most stuff are MSI-like. */ | 819 | /* iommu interrupt handling. Most stuff are MSI-like. */ |
819 | 820 | ||
820 | static const char *fault_reason_strings[] = | 821 | enum faulttype { |
822 | DMA_REMAP, | ||
823 | INTR_REMAP, | ||
824 | UNKNOWN, | ||
825 | }; | ||
826 | |||
827 | static const char *dma_remap_fault_reasons[] = | ||
821 | { | 828 | { |
822 | "Software", | 829 | "Software", |
823 | "Present bit in root entry is clear", | 830 | "Present bit in root entry is clear", |
@@ -833,14 +840,33 @@ static const char *fault_reason_strings[] = | |||
833 | "non-zero reserved fields in CTP", | 840 | "non-zero reserved fields in CTP", |
834 | "non-zero reserved fields in PTE", | 841 | "non-zero reserved fields in PTE", |
835 | }; | 842 | }; |
843 | |||
844 | static const char *intr_remap_fault_reasons[] = | ||
845 | { | ||
846 | "Detected reserved fields in the decoded interrupt-remapped request", | ||
847 | "Interrupt index exceeded the interrupt-remapping table size", | ||
848 | "Present field in the IRTE entry is clear", | ||
849 | "Error accessing interrupt-remapping table pointed by IRTA_REG", | ||
850 | "Detected reserved fields in the IRTE entry", | ||
851 | "Blocked a compatibility format interrupt request", | ||
852 | "Blocked an interrupt request due to source-id verification failure", | ||
853 | }; | ||
854 | |||
836 | #define MAX_FAULT_REASON_IDX (ARRAY_SIZE(fault_reason_strings) - 1) | 855 | #define MAX_FAULT_REASON_IDX (ARRAY_SIZE(fault_reason_strings) - 1) |
837 | 856 | ||
838 | const char *dmar_get_fault_reason(u8 fault_reason) | 857 | const char *dmar_get_fault_reason(u8 fault_reason, int *fault_type) |
839 | { | 858 | { |
840 | if (fault_reason > MAX_FAULT_REASON_IDX) | 859 | if (fault_reason >= 0x20 && (fault_reason <= 0x20 + |
860 | ARRAY_SIZE(intr_remap_fault_reasons))) { | ||
861 | *fault_type = INTR_REMAP; | ||
862 | return intr_remap_fault_reasons[fault_reason - 0x20]; | ||
863 | } else if (fault_reason < ARRAY_SIZE(dma_remap_fault_reasons)) { | ||
864 | *fault_type = DMA_REMAP; | ||
865 | return dma_remap_fault_reasons[fault_reason]; | ||
866 | } else { | ||
867 | *fault_type = UNKNOWN; | ||
841 | return "Unknown"; | 868 | return "Unknown"; |
842 | else | 869 | } |
843 | return fault_reason_strings[fault_reason]; | ||
844 | } | 870 | } |
845 | 871 | ||
846 | void dmar_msi_unmask(unsigned int irq) | 872 | void dmar_msi_unmask(unsigned int irq) |
@@ -897,16 +923,25 @@ static int dmar_fault_do_one(struct intel_iommu *iommu, int type, | |||
897 | u8 fault_reason, u16 source_id, unsigned long long addr) | 923 | u8 fault_reason, u16 source_id, unsigned long long addr) |
898 | { | 924 | { |
899 | const char *reason; | 925 | const char *reason; |
926 | int fault_type; | ||
900 | 927 | ||
901 | reason = dmar_get_fault_reason(fault_reason); | 928 | reason = dmar_get_fault_reason(fault_reason, &fault_type); |
902 | 929 | ||
903 | printk(KERN_ERR | 930 | if (fault_type == INTR_REMAP) |
904 | "DMAR:[%s] Request device [%02x:%02x.%d] " | 931 | printk(KERN_ERR "INTR-REMAP: Request device [[%02x:%02x.%d] " |
905 | "fault addr %llx \n" | 932 | "fault index %llx\n" |
906 | "DMAR:[fault reason %02d] %s\n", | 933 | "INTR-REMAP:[fault reason %02d] %s\n", |
907 | (type ? "DMA Read" : "DMA Write"), | 934 | (source_id >> 8), PCI_SLOT(source_id & 0xFF), |
908 | (source_id >> 8), PCI_SLOT(source_id & 0xFF), | 935 | PCI_FUNC(source_id & 0xFF), addr >> 48, |
909 | PCI_FUNC(source_id & 0xFF), addr, fault_reason, reason); | 936 | fault_reason, reason); |
937 | else | ||
938 | printk(KERN_ERR | ||
939 | "DMAR:[%s] Request device [%02x:%02x.%d] " | ||
940 | "fault addr %llx \n" | ||
941 | "DMAR:[fault reason %02d] %s\n", | ||
942 | (type ? "DMA Read" : "DMA Write"), | ||
943 | (source_id >> 8), PCI_SLOT(source_id & 0xFF), | ||
944 | PCI_FUNC(source_id & 0xFF), addr, fault_reason, reason); | ||
910 | return 0; | 945 | return 0; |
911 | } | 946 | } |
912 | 947 | ||
@@ -920,10 +955,13 @@ static irqreturn_t dmar_fault(int irq, void *dev_id) | |||
920 | 955 | ||
921 | spin_lock_irqsave(&iommu->register_lock, flag); | 956 | spin_lock_irqsave(&iommu->register_lock, flag); |
922 | fault_status = readl(iommu->reg + DMAR_FSTS_REG); | 957 | fault_status = readl(iommu->reg + DMAR_FSTS_REG); |
958 | if (fault_status) | ||
959 | printk(KERN_ERR "DRHD: handling fault status reg %x\n", | ||
960 | fault_status); | ||
923 | 961 | ||
924 | /* TBD: ignore advanced fault log currently */ | 962 | /* TBD: ignore advanced fault log currently */ |
925 | if (!(fault_status & DMA_FSTS_PPF)) | 963 | if (!(fault_status & DMA_FSTS_PPF)) |
926 | goto clear_overflow; | 964 | goto clear_rest; |
927 | 965 | ||
928 | fault_index = dma_fsts_fault_record_index(fault_status); | 966 | fault_index = dma_fsts_fault_record_index(fault_status); |
929 | reg = cap_fault_reg_offset(iommu->cap); | 967 | reg = cap_fault_reg_offset(iommu->cap); |
@@ -964,11 +1002,10 @@ static irqreturn_t dmar_fault(int irq, void *dev_id) | |||
964 | fault_index = 0; | 1002 | fault_index = 0; |
965 | spin_lock_irqsave(&iommu->register_lock, flag); | 1003 | spin_lock_irqsave(&iommu->register_lock, flag); |
966 | } | 1004 | } |
967 | clear_overflow: | 1005 | clear_rest: |
968 | /* clear primary fault overflow */ | 1006 | /* clear all the other faults */ |
969 | fault_status = readl(iommu->reg + DMAR_FSTS_REG); | 1007 | fault_status = readl(iommu->reg + DMAR_FSTS_REG); |
970 | if (fault_status & DMA_FSTS_PFO) | 1008 | writel(fault_status, iommu->reg + DMAR_FSTS_REG); |
971 | writel(DMA_FSTS_PFO, iommu->reg + DMAR_FSTS_REG); | ||
972 | 1009 | ||
973 | spin_unlock_irqrestore(&iommu->register_lock, flag); | 1010 | spin_unlock_irqrestore(&iommu->register_lock, flag); |
974 | return IRQ_HANDLED; | 1011 | return IRQ_HANDLED; |
@@ -978,6 +1015,12 @@ int dmar_set_interrupt(struct intel_iommu *iommu) | |||
978 | { | 1015 | { |
979 | int irq, ret; | 1016 | int irq, ret; |
980 | 1017 | ||
1018 | /* | ||
1019 | * Check if the fault interrupt is already initialized. | ||
1020 | */ | ||
1021 | if (iommu->irq) | ||
1022 | return 0; | ||
1023 | |||
981 | irq = create_irq(); | 1024 | irq = create_irq(); |
982 | if (!irq) { | 1025 | if (!irq) { |
983 | printk(KERN_ERR "IOMMU: no free vectors\n"); | 1026 | printk(KERN_ERR "IOMMU: no free vectors\n"); |
@@ -1003,3 +1046,26 @@ int dmar_set_interrupt(struct intel_iommu *iommu) | |||
1003 | printk(KERN_ERR "IOMMU: can't request irq\n"); | 1046 | printk(KERN_ERR "IOMMU: can't request irq\n"); |
1004 | return ret; | 1047 | return ret; |
1005 | } | 1048 | } |
1049 | |||
1050 | int __init enable_drhd_fault_handling(void) | ||
1051 | { | ||
1052 | struct dmar_drhd_unit *drhd; | ||
1053 | |||
1054 | /* | ||
1055 | * Enable fault control interrupt. | ||
1056 | */ | ||
1057 | for_each_drhd_unit(drhd) { | ||
1058 | int ret; | ||
1059 | struct intel_iommu *iommu = drhd->iommu; | ||
1060 | ret = dmar_set_interrupt(iommu); | ||
1061 | |||
1062 | if (ret) { | ||
1063 | printk(KERN_ERR "DRHD %Lx: failed to enable fault, " | ||
1064 | " interrupt, ret %d\n", | ||
1065 | (unsigned long long)drhd->reg_base_addr, ret); | ||
1066 | return -1; | ||
1067 | } | ||
1068 | } | ||
1069 | |||
1070 | return 0; | ||
1071 | } | ||