diff options
author | Suresh Siddha <suresh.b.siddha@intel.com> | 2009-03-16 20:04:55 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2009-03-17 18:38:59 -0400 |
commit | 9d783ba042771284fb4ee5013c3d94220755ae7f (patch) | |
tree | 102ec9f89d363589108ae35e4b38c12fc6e2765c | |
parent | 0ac2491f57af5644f88383d28809760902d6f4d7 (diff) |
x86, x2apic: enable fault handling for intr-remapping
Impact: interface augmentation (not yet used)
Enable fault handling flow for intr-remapping aswell. Fault handling
code now shared by both dma-remapping and intr-remapping.
Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-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 6706b3006f13..4cc48af23fef 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 00e6071cefc4..b18a7734d689 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 8d7748efe6a8..8297c2b8ed20 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 75d34bf2db50..bb4ed985f9c7 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 4a4ab651b709..25fc1df486bb 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 5ffa65fffb6a..c38e3f437a81 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 f28440784cf0..c7768330c11d 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 d2e3cbfba14f..a9563840644b 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 */ |