diff options
| -rw-r--r-- | drivers/pci/dmar.c | 191 | ||||
| -rw-r--r-- | drivers/pci/intel-iommu.c | 188 |
2 files changed, 191 insertions, 188 deletions
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 5f333403c2ea..75d34bf2db50 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:" |
| @@ -812,3 +814,192 @@ int dmar_enable_qi(struct intel_iommu *iommu) | |||
| 812 | 814 | ||
| 813 | return 0; | 815 | return 0; |
| 814 | } | 816 | } |
| 817 | |||
| 818 | /* iommu interrupt handling. Most stuff are MSI-like. */ | ||
| 819 | |||
| 820 | static const char *fault_reason_strings[] = | ||
| 821 | { | ||
| 822 | "Software", | ||
| 823 | "Present bit in root entry is clear", | ||
| 824 | "Present bit in context entry is clear", | ||
| 825 | "Invalid context entry", | ||
| 826 | "Access beyond MGAW", | ||
| 827 | "PTE Write access is not set", | ||
| 828 | "PTE Read access is not set", | ||
| 829 | "Next page table ptr is invalid", | ||
| 830 | "Root table address invalid", | ||
| 831 | "Context table ptr is invalid", | ||
| 832 | "non-zero reserved fields in RTP", | ||
| 833 | "non-zero reserved fields in CTP", | ||
| 834 | "non-zero reserved fields in PTE", | ||
| 835 | }; | ||
| 836 | #define MAX_FAULT_REASON_IDX (ARRAY_SIZE(fault_reason_strings) - 1) | ||
| 837 | |||
| 838 | const char *dmar_get_fault_reason(u8 fault_reason) | ||
| 839 | { | ||
| 840 | if (fault_reason > MAX_FAULT_REASON_IDX) | ||
| 841 | return "Unknown"; | ||
| 842 | else | ||
| 843 | return fault_reason_strings[fault_reason]; | ||
| 844 | } | ||
| 845 | |||
| 846 | void dmar_msi_unmask(unsigned int irq) | ||
| 847 | { | ||
| 848 | struct intel_iommu *iommu = get_irq_data(irq); | ||
| 849 | unsigned long flag; | ||
| 850 | |||
| 851 | /* unmask it */ | ||
| 852 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
| 853 | writel(0, iommu->reg + DMAR_FECTL_REG); | ||
| 854 | /* Read a reg to force flush the post write */ | ||
| 855 | readl(iommu->reg + DMAR_FECTL_REG); | ||
| 856 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
| 857 | } | ||
| 858 | |||
| 859 | void dmar_msi_mask(unsigned int irq) | ||
| 860 | { | ||
| 861 | unsigned long flag; | ||
| 862 | struct intel_iommu *iommu = get_irq_data(irq); | ||
| 863 | |||
| 864 | /* mask it */ | ||
| 865 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
| 866 | writel(DMA_FECTL_IM, iommu->reg + DMAR_FECTL_REG); | ||
| 867 | /* Read a reg to force flush the post write */ | ||
| 868 | readl(iommu->reg + DMAR_FECTL_REG); | ||
| 869 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
| 870 | } | ||
| 871 | |||
| 872 | void dmar_msi_write(int irq, struct msi_msg *msg) | ||
| 873 | { | ||
| 874 | struct intel_iommu *iommu = get_irq_data(irq); | ||
| 875 | unsigned long flag; | ||
| 876 | |||
| 877 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
| 878 | writel(msg->data, iommu->reg + DMAR_FEDATA_REG); | ||
| 879 | writel(msg->address_lo, iommu->reg + DMAR_FEADDR_REG); | ||
| 880 | writel(msg->address_hi, iommu->reg + DMAR_FEUADDR_REG); | ||
| 881 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
| 882 | } | ||
| 883 | |||
| 884 | void dmar_msi_read(int irq, struct msi_msg *msg) | ||
| 885 | { | ||
| 886 | struct intel_iommu *iommu = get_irq_data(irq); | ||
| 887 | unsigned long flag; | ||
| 888 | |||
| 889 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
| 890 | msg->data = readl(iommu->reg + DMAR_FEDATA_REG); | ||
| 891 | msg->address_lo = readl(iommu->reg + DMAR_FEADDR_REG); | ||
| 892 | msg->address_hi = readl(iommu->reg + DMAR_FEUADDR_REG); | ||
| 893 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
| 894 | } | ||
| 895 | |||
| 896 | static int dmar_fault_do_one(struct intel_iommu *iommu, int type, | ||
| 897 | u8 fault_reason, u16 source_id, unsigned long long addr) | ||
| 898 | { | ||
| 899 | const char *reason; | ||
| 900 | |||
| 901 | reason = dmar_get_fault_reason(fault_reason); | ||
| 902 | |||
| 903 | printk(KERN_ERR | ||
| 904 | "DMAR:[%s] Request device [%02x:%02x.%d] " | ||
| 905 | "fault addr %llx \n" | ||
| 906 | "DMAR:[fault reason %02d] %s\n", | ||
| 907 | (type ? "DMA Read" : "DMA Write"), | ||
| 908 | (source_id >> 8), PCI_SLOT(source_id & 0xFF), | ||
| 909 | PCI_FUNC(source_id & 0xFF), addr, fault_reason, reason); | ||
| 910 | return 0; | ||
| 911 | } | ||
| 912 | |||
| 913 | #define PRIMARY_FAULT_REG_LEN (16) | ||
| 914 | static irqreturn_t dmar_fault(int irq, void *dev_id) | ||
| 915 | { | ||
| 916 | struct intel_iommu *iommu = dev_id; | ||
| 917 | int reg, fault_index; | ||
| 918 | u32 fault_status; | ||
| 919 | unsigned long flag; | ||
| 920 | |||
| 921 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
| 922 | fault_status = readl(iommu->reg + DMAR_FSTS_REG); | ||
| 923 | |||
| 924 | /* TBD: ignore advanced fault log currently */ | ||
| 925 | if (!(fault_status & DMA_FSTS_PPF)) | ||
| 926 | goto clear_overflow; | ||
| 927 | |||
| 928 | fault_index = dma_fsts_fault_record_index(fault_status); | ||
| 929 | reg = cap_fault_reg_offset(iommu->cap); | ||
| 930 | while (1) { | ||
| 931 | u8 fault_reason; | ||
| 932 | u16 source_id; | ||
| 933 | u64 guest_addr; | ||
| 934 | int type; | ||
| 935 | u32 data; | ||
| 936 | |||
| 937 | /* highest 32 bits */ | ||
| 938 | data = readl(iommu->reg + reg + | ||
| 939 | fault_index * PRIMARY_FAULT_REG_LEN + 12); | ||
| 940 | if (!(data & DMA_FRCD_F)) | ||
| 941 | break; | ||
| 942 | |||
| 943 | fault_reason = dma_frcd_fault_reason(data); | ||
| 944 | type = dma_frcd_type(data); | ||
| 945 | |||
| 946 | data = readl(iommu->reg + reg + | ||
| 947 | fault_index * PRIMARY_FAULT_REG_LEN + 8); | ||
| 948 | source_id = dma_frcd_source_id(data); | ||
| 949 | |||
| 950 | guest_addr = dmar_readq(iommu->reg + reg + | ||
| 951 | fault_index * PRIMARY_FAULT_REG_LEN); | ||
| 952 | guest_addr = dma_frcd_page_addr(guest_addr); | ||
| 953 | /* clear the fault */ | ||
| 954 | writel(DMA_FRCD_F, iommu->reg + reg + | ||
| 955 | fault_index * PRIMARY_FAULT_REG_LEN + 12); | ||
| 956 | |||
| 957 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
| 958 | |||
| 959 | dmar_fault_do_one(iommu, type, fault_reason, | ||
| 960 | source_id, guest_addr); | ||
| 961 | |||
| 962 | fault_index++; | ||
| 963 | if (fault_index > cap_num_fault_regs(iommu->cap)) | ||
| 964 | fault_index = 0; | ||
| 965 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
| 966 | } | ||
| 967 | clear_overflow: | ||
| 968 | /* clear primary fault overflow */ | ||
| 969 | fault_status = readl(iommu->reg + DMAR_FSTS_REG); | ||
| 970 | if (fault_status & DMA_FSTS_PFO) | ||
| 971 | writel(DMA_FSTS_PFO, iommu->reg + DMAR_FSTS_REG); | ||
| 972 | |||
| 973 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
| 974 | return IRQ_HANDLED; | ||
| 975 | } | ||
| 976 | |||
| 977 | int dmar_set_interrupt(struct intel_iommu *iommu) | ||
| 978 | { | ||
| 979 | int irq, ret; | ||
| 980 | |||
| 981 | irq = create_irq(); | ||
| 982 | if (!irq) { | ||
| 983 | printk(KERN_ERR "IOMMU: no free vectors\n"); | ||
| 984 | return -EINVAL; | ||
| 985 | } | ||
| 986 | |||
| 987 | set_irq_data(irq, iommu); | ||
| 988 | iommu->irq = irq; | ||
| 989 | |||
| 990 | ret = arch_setup_dmar_msi(irq); | ||
| 991 | if (ret) { | ||
| 992 | set_irq_data(irq, NULL); | ||
| 993 | iommu->irq = 0; | ||
| 994 | destroy_irq(irq); | ||
| 995 | return 0; | ||
| 996 | } | ||
| 997 | |||
| 998 | /* Force fault register is cleared */ | ||
| 999 | dmar_fault(irq, iommu); | ||
| 1000 | |||
| 1001 | ret = request_irq(irq, dmar_fault, 0, iommu->name, iommu); | ||
| 1002 | if (ret) | ||
| 1003 | printk(KERN_ERR "IOMMU: can't request irq\n"); | ||
| 1004 | return ret; | ||
| 1005 | } | ||
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index f3f686581a90..4a4ab651b709 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c | |||
| @@ -1004,194 +1004,6 @@ static int iommu_disable_translation(struct intel_iommu *iommu) | |||
| 1004 | return 0; | 1004 | return 0; |
| 1005 | } | 1005 | } |
| 1006 | 1006 | ||
| 1007 | /* iommu interrupt handling. Most stuff are MSI-like. */ | ||
| 1008 | |||
| 1009 | static const char *fault_reason_strings[] = | ||
| 1010 | { | ||
| 1011 | "Software", | ||
| 1012 | "Present bit in root entry is clear", | ||
| 1013 | "Present bit in context entry is clear", | ||
| 1014 | "Invalid context entry", | ||
| 1015 | "Access beyond MGAW", | ||
| 1016 | "PTE Write access is not set", | ||
| 1017 | "PTE Read access is not set", | ||
| 1018 | "Next page table ptr is invalid", | ||
| 1019 | "Root table address invalid", | ||
| 1020 | "Context table ptr is invalid", | ||
| 1021 | "non-zero reserved fields in RTP", | ||
| 1022 | "non-zero reserved fields in CTP", | ||
| 1023 | "non-zero reserved fields in PTE", | ||
| 1024 | }; | ||
| 1025 | #define MAX_FAULT_REASON_IDX (ARRAY_SIZE(fault_reason_strings) - 1) | ||
| 1026 | |||
| 1027 | const char *dmar_get_fault_reason(u8 fault_reason) | ||
| 1028 | { | ||
| 1029 | if (fault_reason > MAX_FAULT_REASON_IDX) | ||
| 1030 | return "Unknown"; | ||
| 1031 | else | ||
| 1032 | return fault_reason_strings[fault_reason]; | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | void dmar_msi_unmask(unsigned int irq) | ||
| 1036 | { | ||
| 1037 | struct intel_iommu *iommu = get_irq_data(irq); | ||
| 1038 | unsigned long flag; | ||
| 1039 | |||
| 1040 | /* unmask it */ | ||
| 1041 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
| 1042 | writel(0, iommu->reg + DMAR_FECTL_REG); | ||
| 1043 | /* Read a reg to force flush the post write */ | ||
| 1044 | readl(iommu->reg + DMAR_FECTL_REG); | ||
| 1045 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | void dmar_msi_mask(unsigned int irq) | ||
| 1049 | { | ||
| 1050 | unsigned long flag; | ||
| 1051 | struct intel_iommu *iommu = get_irq_data(irq); | ||
| 1052 | |||
| 1053 | /* mask it */ | ||
| 1054 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
| 1055 | writel(DMA_FECTL_IM, iommu->reg + DMAR_FECTL_REG); | ||
| 1056 | /* Read a reg to force flush the post write */ | ||
| 1057 | readl(iommu->reg + DMAR_FECTL_REG); | ||
| 1058 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | void dmar_msi_write(int irq, struct msi_msg *msg) | ||
| 1062 | { | ||
| 1063 | struct intel_iommu *iommu = get_irq_data(irq); | ||
| 1064 | unsigned long flag; | ||
| 1065 | |||
| 1066 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
| 1067 | writel(msg->data, iommu->reg + DMAR_FEDATA_REG); | ||
| 1068 | writel(msg->address_lo, iommu->reg + DMAR_FEADDR_REG); | ||
| 1069 | writel(msg->address_hi, iommu->reg + DMAR_FEUADDR_REG); | ||
| 1070 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | void dmar_msi_read(int irq, struct msi_msg *msg) | ||
| 1074 | { | ||
| 1075 | struct intel_iommu *iommu = get_irq_data(irq); | ||
| 1076 | unsigned long flag; | ||
| 1077 | |||
| 1078 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
| 1079 | msg->data = readl(iommu->reg + DMAR_FEDATA_REG); | ||
| 1080 | msg->address_lo = readl(iommu->reg + DMAR_FEADDR_REG); | ||
| 1081 | msg->address_hi = readl(iommu->reg + DMAR_FEUADDR_REG); | ||
| 1082 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | static int iommu_page_fault_do_one(struct intel_iommu *iommu, int type, | ||
| 1086 | u8 fault_reason, u16 source_id, unsigned long long addr) | ||
| 1087 | { | ||
| 1088 | const char *reason; | ||
| 1089 | |||
| 1090 | reason = dmar_get_fault_reason(fault_reason); | ||
| 1091 | |||
| 1092 | printk(KERN_ERR | ||
| 1093 | "DMAR:[%s] Request device [%02x:%02x.%d] " | ||
| 1094 | "fault addr %llx \n" | ||
| 1095 | "DMAR:[fault reason %02d] %s\n", | ||
| 1096 | (type ? "DMA Read" : "DMA Write"), | ||
| 1097 | (source_id >> 8), PCI_SLOT(source_id & 0xFF), | ||
| 1098 | PCI_FUNC(source_id & 0xFF), addr, fault_reason, reason); | ||
| 1099 | return 0; | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | #define PRIMARY_FAULT_REG_LEN (16) | ||
| 1103 | static irqreturn_t iommu_page_fault(int irq, void *dev_id) | ||
| 1104 | { | ||
| 1105 | struct intel_iommu *iommu = dev_id; | ||
| 1106 | int reg, fault_index; | ||
| 1107 | u32 fault_status; | ||
| 1108 | unsigned long flag; | ||
| 1109 | |||
| 1110 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
| 1111 | fault_status = readl(iommu->reg + DMAR_FSTS_REG); | ||
| 1112 | |||
| 1113 | /* TBD: ignore advanced fault log currently */ | ||
| 1114 | if (!(fault_status & DMA_FSTS_PPF)) | ||
| 1115 | goto clear_overflow; | ||
| 1116 | |||
| 1117 | fault_index = dma_fsts_fault_record_index(fault_status); | ||
| 1118 | reg = cap_fault_reg_offset(iommu->cap); | ||
| 1119 | while (1) { | ||
| 1120 | u8 fault_reason; | ||
| 1121 | u16 source_id; | ||
| 1122 | u64 guest_addr; | ||
| 1123 | int type; | ||
| 1124 | u32 data; | ||
| 1125 | |||
| 1126 | /* highest 32 bits */ | ||
| 1127 | data = readl(iommu->reg + reg + | ||
| 1128 | fault_index * PRIMARY_FAULT_REG_LEN + 12); | ||
| 1129 | if (!(data & DMA_FRCD_F)) | ||
| 1130 | break; | ||
| 1131 | |||
| 1132 | fault_reason = dma_frcd_fault_reason(data); | ||
| 1133 | type = dma_frcd_type(data); | ||
| 1134 | |||
| 1135 | data = readl(iommu->reg + reg + | ||
| 1136 | fault_index * PRIMARY_FAULT_REG_LEN + 8); | ||
| 1137 | source_id = dma_frcd_source_id(data); | ||
| 1138 | |||
| 1139 | guest_addr = dmar_readq(iommu->reg + reg + | ||
| 1140 | fault_index * PRIMARY_FAULT_REG_LEN); | ||
| 1141 | guest_addr = dma_frcd_page_addr(guest_addr); | ||
| 1142 | /* clear the fault */ | ||
| 1143 | writel(DMA_FRCD_F, iommu->reg + reg + | ||
| 1144 | fault_index * PRIMARY_FAULT_REG_LEN + 12); | ||
| 1145 | |||
| 1146 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
| 1147 | |||
| 1148 | iommu_page_fault_do_one(iommu, type, fault_reason, | ||
| 1149 | source_id, guest_addr); | ||
| 1150 | |||
| 1151 | fault_index++; | ||
| 1152 | if (fault_index > cap_num_fault_regs(iommu->cap)) | ||
| 1153 | fault_index = 0; | ||
| 1154 | spin_lock_irqsave(&iommu->register_lock, flag); | ||
| 1155 | } | ||
| 1156 | clear_overflow: | ||
| 1157 | /* clear primary fault overflow */ | ||
| 1158 | fault_status = readl(iommu->reg + DMAR_FSTS_REG); | ||
| 1159 | if (fault_status & DMA_FSTS_PFO) | ||
| 1160 | writel(DMA_FSTS_PFO, iommu->reg + DMAR_FSTS_REG); | ||
| 1161 | |||
| 1162 | spin_unlock_irqrestore(&iommu->register_lock, flag); | ||
| 1163 | return IRQ_HANDLED; | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | int dmar_set_interrupt(struct intel_iommu *iommu) | ||
| 1167 | { | ||
| 1168 | int irq, ret; | ||
| 1169 | |||
| 1170 | irq = create_irq(); | ||
| 1171 | if (!irq) { | ||
| 1172 | printk(KERN_ERR "IOMMU: no free vectors\n"); | ||
| 1173 | return -EINVAL; | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | set_irq_data(irq, iommu); | ||
| 1177 | iommu->irq = irq; | ||
| 1178 | |||
| 1179 | ret = arch_setup_dmar_msi(irq); | ||
| 1180 | if (ret) { | ||
| 1181 | set_irq_data(irq, NULL); | ||
| 1182 | iommu->irq = 0; | ||
| 1183 | destroy_irq(irq); | ||
| 1184 | return 0; | ||
| 1185 | } | ||
| 1186 | |||
| 1187 | /* Force fault register is cleared */ | ||
| 1188 | iommu_page_fault(irq, iommu); | ||
| 1189 | |||
| 1190 | ret = request_irq(irq, iommu_page_fault, 0, iommu->name, iommu); | ||
| 1191 | if (ret) | ||
| 1192 | printk(KERN_ERR "IOMMU: can't request irq\n"); | ||
| 1193 | return ret; | ||
| 1194 | } | ||
| 1195 | 1007 | ||
| 1196 | static int iommu_init_domains(struct intel_iommu *iommu) | 1008 | static int iommu_init_domains(struct intel_iommu *iommu) |
| 1197 | { | 1009 | { |
