diff options
author | Suresh Siddha <suresh.b.siddha@intel.com> | 2009-03-16 20:04:54 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2009-03-17 18:37:06 -0400 |
commit | 0ac2491f57af5644f88383d28809760902d6f4d7 (patch) | |
tree | 0dcf5875ef83a5bd14cbe37f8b4671a4601cc797 /drivers/pci/dmar.c | |
parent | 4c5502b1c5744b2090414e1b80ca6388d5c46e06 (diff) |
x86, dmar: move page fault handling code to dmar.c
Impact: code movement
Move page fault handling code to dmar.c
This will be shared both by DMA-remapping and Intr-remapping code.
Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'drivers/pci/dmar.c')
-rw-r--r-- | drivers/pci/dmar.c | 191 |
1 files changed, 191 insertions, 0 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 | } | ||