diff options
| -rw-r--r-- | arch/x86/include/asm/msidef.h | 1 | ||||
| -rw-r--r-- | arch/x86/kernel/apic/io_apic.c | 9 | ||||
| -rw-r--r-- | arch/x86/kernel/apic/probe_64.c | 9 | ||||
| -rw-r--r-- | drivers/pci/dmar.c | 102 | ||||
| -rw-r--r-- | drivers/pci/intel-iommu.c | 3 | ||||
| -rw-r--r-- | drivers/pci/intr_remapping.c | 2 | ||||
| -rw-r--r-- | include/linux/dmar.h | 5 | ||||
| -rw-r--r-- | include/linux/intel-iommu.h | 4 |
8 files changed, 107 insertions, 28 deletions
diff --git a/arch/x86/include/asm/msidef.h b/arch/x86/include/asm/msidef.h index 6706b3006f1..4cc48af23fe 100644 --- a/arch/x86/include/asm/msidef.h +++ b/arch/x86/include/asm/msidef.h | |||
| @@ -47,6 +47,7 @@ | |||
| 47 | #define MSI_ADDR_DEST_ID_MASK 0x00ffff0 | 47 | #define MSI_ADDR_DEST_ID_MASK 0x00ffff0 |
| 48 | #define MSI_ADDR_DEST_ID(dest) (((dest) << MSI_ADDR_DEST_ID_SHIFT) & \ | 48 | #define MSI_ADDR_DEST_ID(dest) (((dest) << MSI_ADDR_DEST_ID_SHIFT) & \ |
| 49 | MSI_ADDR_DEST_ID_MASK) | 49 | MSI_ADDR_DEST_ID_MASK) |
| 50 | #define MSI_ADDR_EXT_DEST_ID(dest) ((dest) & 0xffffff00) | ||
| 50 | 51 | ||
| 51 | #define MSI_ADDR_IR_EXT_INT (1 << 4) | 52 | #define MSI_ADDR_IR_EXT_INT (1 << 4) |
| 52 | #define MSI_ADDR_IR_SHV (1 << 3) | 53 | #define MSI_ADDR_IR_SHV (1 << 3) |
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index 00e6071cefc..b18a7734d68 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c | |||
| @@ -3294,7 +3294,12 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_ms | |||
| 3294 | } else | 3294 | } else |
| 3295 | #endif | 3295 | #endif |
| 3296 | { | 3296 | { |
| 3297 | msg->address_hi = MSI_ADDR_BASE_HI; | 3297 | if (x2apic_enabled()) |
| 3298 | msg->address_hi = MSI_ADDR_BASE_HI | | ||
| 3299 | MSI_ADDR_EXT_DEST_ID(dest); | ||
| 3300 | else | ||
| 3301 | msg->address_hi = MSI_ADDR_BASE_HI; | ||
| 3302 | |||
| 3298 | msg->address_lo = | 3303 | msg->address_lo = |
| 3299 | MSI_ADDR_BASE_LO | | 3304 | MSI_ADDR_BASE_LO | |
| 3300 | ((apic->irq_dest_mode == 0) ? | 3305 | ((apic->irq_dest_mode == 0) ? |
| @@ -3528,7 +3533,7 @@ void arch_teardown_msi_irq(unsigned int irq) | |||
| 3528 | destroy_irq(irq); | 3533 | destroy_irq(irq); |
| 3529 | } | 3534 | } |
| 3530 | 3535 | ||
| 3531 | #ifdef CONFIG_DMAR | 3536 | #if defined (CONFIG_DMAR) || defined (CONFIG_INTR_REMAP) |
| 3532 | #ifdef CONFIG_SMP | 3537 | #ifdef CONFIG_SMP |
| 3533 | static void dmar_msi_set_affinity(unsigned int irq, const struct cpumask *mask) | 3538 | static void dmar_msi_set_affinity(unsigned int irq, const struct cpumask *mask) |
| 3534 | { | 3539 | { |
diff --git a/arch/x86/kernel/apic/probe_64.c b/arch/x86/kernel/apic/probe_64.c index 8d7748efe6a..8297c2b8ed2 100644 --- a/arch/x86/kernel/apic/probe_64.c +++ b/arch/x86/kernel/apic/probe_64.c | |||
| @@ -68,6 +68,15 @@ void __init default_setup_apic_routing(void) | |||
| 68 | apic = &apic_physflat; | 68 | apic = &apic_physflat; |
| 69 | printk(KERN_INFO "Setting APIC routing to %s\n", apic->name); | 69 | printk(KERN_INFO "Setting APIC routing to %s\n", apic->name); |
| 70 | } | 70 | } |
| 71 | |||
| 72 | #ifdef CONFIG_X86_X2APIC | ||
| 73 | /* | ||
| 74 | * Now that apic routing model is selected, configure the | ||
| 75 | * fault handling for intr remapping. | ||
| 76 | */ | ||
| 77 | if (intr_remapping_enabled) | ||
| 78 | enable_drhd_fault_handling(); | ||
| 79 | #endif | ||
| 71 | } | 80 | } |
| 72 | 81 | ||
| 73 | /* Same for both flat and physical. */ | 82 | /* Same for both flat and physical. */ |
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 | } | ||
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 4a4ab651b70..25fc1df486b 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c | |||
| @@ -1799,7 +1799,7 @@ static int __init init_dmars(void) | |||
| 1799 | struct dmar_rmrr_unit *rmrr; | 1799 | struct dmar_rmrr_unit *rmrr; |
| 1800 | struct pci_dev *pdev; | 1800 | struct pci_dev *pdev; |
| 1801 | struct intel_iommu *iommu; | 1801 | struct intel_iommu *iommu; |
| 1802 | int i, ret, unit = 0; | 1802 | int i, ret; |
| 1803 | 1803 | ||
| 1804 | /* | 1804 | /* |
| 1805 | * for each drhd | 1805 | * for each drhd |
| @@ -1921,7 +1921,6 @@ static int __init init_dmars(void) | |||
| 1921 | if (drhd->ignored) | 1921 | if (drhd->ignored) |
| 1922 | continue; | 1922 | continue; |
| 1923 | iommu = drhd->iommu; | 1923 | iommu = drhd->iommu; |
| 1924 | sprintf (iommu->name, "dmar%d", unit++); | ||
| 1925 | 1924 | ||
| 1926 | iommu_flush_write_buffer(iommu); | 1925 | iommu_flush_write_buffer(iommu); |
| 1927 | 1926 | ||
diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index 5ffa65fffb6..c38e3f437a8 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c | |||
| @@ -308,7 +308,7 @@ int modify_irte(int irq, struct irte *irte_modified) | |||
| 308 | index = irq_iommu->irte_index + irq_iommu->sub_handle; | 308 | index = irq_iommu->irte_index + irq_iommu->sub_handle; |
| 309 | irte = &iommu->ir_table->base[index]; | 309 | irte = &iommu->ir_table->base[index]; |
| 310 | 310 | ||
| 311 | set_64bit((unsigned long *)irte, irte_modified->low | (1 << 1)); | 311 | set_64bit((unsigned long *)irte, irte_modified->low); |
| 312 | __iommu_flush_cache(iommu, irte, sizeof(*irte)); | 312 | __iommu_flush_cache(iommu, irte, sizeof(*irte)); |
| 313 | 313 | ||
| 314 | rc = qi_flush_iec(iommu, index, 0); | 314 | rc = qi_flush_iec(iommu, index, 0); |
diff --git a/include/linux/dmar.h b/include/linux/dmar.h index f28440784cf..c7768330c11 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h | |||
| @@ -49,6 +49,7 @@ extern int dmar_dev_scope_init(void); | |||
| 49 | 49 | ||
| 50 | /* Intel IOMMU detection */ | 50 | /* Intel IOMMU detection */ |
| 51 | extern void detect_intel_iommu(void); | 51 | extern void detect_intel_iommu(void); |
| 52 | extern int enable_drhd_fault_handling(void); | ||
| 52 | 53 | ||
| 53 | 54 | ||
| 54 | extern int parse_ioapics_under_ir(void); | 55 | extern int parse_ioapics_under_ir(void); |
| @@ -116,9 +117,6 @@ extern struct intel_iommu *map_ioapic_to_ir(int apic); | |||
| 116 | #define intr_remapping_enabled (0) | 117 | #define intr_remapping_enabled (0) |
| 117 | #endif | 118 | #endif |
| 118 | 119 | ||
| 119 | #ifdef CONFIG_DMAR | ||
| 120 | extern const char *dmar_get_fault_reason(u8 fault_reason); | ||
| 121 | |||
| 122 | /* Can't use the common MSI interrupt functions | 120 | /* Can't use the common MSI interrupt functions |
| 123 | * since DMAR is not a pci device | 121 | * since DMAR is not a pci device |
| 124 | */ | 122 | */ |
| @@ -129,6 +127,7 @@ extern void dmar_msi_write(int irq, struct msi_msg *msg); | |||
| 129 | extern int dmar_set_interrupt(struct intel_iommu *iommu); | 127 | extern int dmar_set_interrupt(struct intel_iommu *iommu); |
| 130 | extern int arch_setup_dmar_msi(unsigned int irq); | 128 | extern int arch_setup_dmar_msi(unsigned int irq); |
| 131 | 129 | ||
| 130 | #ifdef CONFIG_DMAR | ||
| 132 | extern int iommu_detected, no_iommu; | 131 | extern int iommu_detected, no_iommu; |
| 133 | extern struct list_head dmar_rmrr_units; | 132 | extern struct list_head dmar_rmrr_units; |
| 134 | struct dmar_rmrr_unit { | 133 | struct dmar_rmrr_unit { |
diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index d2e3cbfba14..a9563840644 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h | |||
| @@ -292,6 +292,8 @@ struct intel_iommu { | |||
| 292 | spinlock_t register_lock; /* protect register handling */ | 292 | spinlock_t register_lock; /* protect register handling */ |
| 293 | int seq_id; /* sequence id of the iommu */ | 293 | int seq_id; /* sequence id of the iommu */ |
| 294 | int agaw; /* agaw of this iommu */ | 294 | int agaw; /* agaw of this iommu */ |
| 295 | unsigned int irq; | ||
| 296 | unsigned char name[13]; /* Device Name */ | ||
| 295 | 297 | ||
| 296 | #ifdef CONFIG_DMAR | 298 | #ifdef CONFIG_DMAR |
| 297 | unsigned long *domain_ids; /* bitmap of domains */ | 299 | unsigned long *domain_ids; /* bitmap of domains */ |
| @@ -299,8 +301,6 @@ struct intel_iommu { | |||
| 299 | spinlock_t lock; /* protect context, domain ids */ | 301 | spinlock_t lock; /* protect context, domain ids */ |
| 300 | struct root_entry *root_entry; /* virtual address */ | 302 | struct root_entry *root_entry; /* virtual address */ |
| 301 | 303 | ||
| 302 | unsigned int irq; | ||
| 303 | unsigned char name[7]; /* Device Name */ | ||
| 304 | struct iommu_flush flush; | 304 | struct iommu_flush flush; |
| 305 | #endif | 305 | #endif |
| 306 | struct q_inval *qi; /* Queued invalidation info */ | 306 | struct q_inval *qi; /* Queued invalidation info */ |
