diff options
author | Suresh Siddha <suresh.b.siddha@intel.com> | 2008-07-10 14:16:42 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-07-12 02:44:52 -0400 |
commit | fe962e90cb17a8426e144dee970e77ed789d98ee (patch) | |
tree | c7b3343df9bf58e047333758a89c78f6615fb97b /drivers/pci | |
parent | cf1337f0447e5be8e66daa944f0ea3bcac2b6179 (diff) |
x64, x2apic/intr-remap: Queued invalidation infrastructure (part of VT-d)
Queued invalidation (part of Intel Virtualization Technology for
Directed I/O architecture) infrastructure.
This will be used for invalidating the interrupt entry cache in the
case of Interrupt-remapping and IOTLB invalidation in the case
of DMA-remapping.
Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
Cc: akpm@linux-foundation.org
Cc: arjan@linux.intel.com
Cc: andi@firstfloor.org
Cc: ebiederm@xmission.com
Cc: jbarnes@virtuousgeek.org
Cc: steiner@sgi.com
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/dmar.c | 150 | ||||
-rw-r--r-- | drivers/pci/intel-iommu.c | 7 | ||||
-rw-r--r-- | drivers/pci/intel-iommu.h | 61 |
3 files changed, 211 insertions, 7 deletions
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 127764cfbe27..aba151ca6d26 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c | |||
@@ -28,6 +28,7 @@ | |||
28 | 28 | ||
29 | #include <linux/pci.h> | 29 | #include <linux/pci.h> |
30 | #include <linux/dmar.h> | 30 | #include <linux/dmar.h> |
31 | #include <linux/timer.h> | ||
31 | #include "iova.h" | 32 | #include "iova.h" |
32 | #include "intel-iommu.h" | 33 | #include "intel-iommu.h" |
33 | 34 | ||
@@ -509,3 +510,152 @@ void free_iommu(struct intel_iommu *iommu) | |||
509 | iounmap(iommu->reg); | 510 | iounmap(iommu->reg); |
510 | kfree(iommu); | 511 | kfree(iommu); |
511 | } | 512 | } |
513 | |||
514 | /* | ||
515 | * Reclaim all the submitted descriptors which have completed its work. | ||
516 | */ | ||
517 | static inline void reclaim_free_desc(struct q_inval *qi) | ||
518 | { | ||
519 | while (qi->desc_status[qi->free_tail] == QI_DONE) { | ||
520 | qi->desc_status[qi->free_tail] = QI_FREE; | ||
521 | qi->free_tail = (qi->free_tail + 1) % QI_LENGTH; | ||
522 | qi->free_cnt++; | ||
523 | } | ||
524 | } | ||
525 | |||
526 | /* | ||
527 | * Submit the queued invalidation descriptor to the remapping | ||
528 | * hardware unit and wait for its completion. | ||
529 | */ | ||
530 | void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu) | ||
531 | { | ||
532 | struct q_inval *qi = iommu->qi; | ||
533 | struct qi_desc *hw, wait_desc; | ||
534 | int wait_index, index; | ||
535 | unsigned long flags; | ||
536 | |||
537 | if (!qi) | ||
538 | return; | ||
539 | |||
540 | hw = qi->desc; | ||
541 | |||
542 | spin_lock(&qi->q_lock); | ||
543 | while (qi->free_cnt < 3) { | ||
544 | spin_unlock(&qi->q_lock); | ||
545 | cpu_relax(); | ||
546 | spin_lock(&qi->q_lock); | ||
547 | } | ||
548 | |||
549 | index = qi->free_head; | ||
550 | wait_index = (index + 1) % QI_LENGTH; | ||
551 | |||
552 | qi->desc_status[index] = qi->desc_status[wait_index] = QI_IN_USE; | ||
553 | |||
554 | hw[index] = *desc; | ||
555 | |||
556 | wait_desc.low = QI_IWD_STATUS_DATA(2) | QI_IWD_STATUS_WRITE | QI_IWD_TYPE; | ||
557 | wait_desc.high = virt_to_phys(&qi->desc_status[wait_index]); | ||
558 | |||
559 | hw[wait_index] = wait_desc; | ||
560 | |||
561 | __iommu_flush_cache(iommu, &hw[index], sizeof(struct qi_desc)); | ||
562 | __iommu_flush_cache(iommu, &hw[wait_index], sizeof(struct qi_desc)); | ||
563 | |||
564 | qi->free_head = (qi->free_head + 2) % QI_LENGTH; | ||
565 | qi->free_cnt -= 2; | ||
566 | |||
567 | spin_lock_irqsave(&iommu->register_lock, flags); | ||
568 | /* | ||
569 | * update the HW tail register indicating the presence of | ||
570 | * new descriptors. | ||
571 | */ | ||
572 | writel(qi->free_head << 4, iommu->reg + DMAR_IQT_REG); | ||
573 | spin_unlock_irqrestore(&iommu->register_lock, flags); | ||
574 | |||
575 | while (qi->desc_status[wait_index] != QI_DONE) { | ||
576 | spin_unlock(&qi->q_lock); | ||
577 | cpu_relax(); | ||
578 | spin_lock(&qi->q_lock); | ||
579 | } | ||
580 | |||
581 | qi->desc_status[index] = QI_DONE; | ||
582 | |||
583 | reclaim_free_desc(qi); | ||
584 | spin_unlock(&qi->q_lock); | ||
585 | } | ||
586 | |||
587 | /* | ||
588 | * Flush the global interrupt entry cache. | ||
589 | */ | ||
590 | void qi_global_iec(struct intel_iommu *iommu) | ||
591 | { | ||
592 | struct qi_desc desc; | ||
593 | |||
594 | desc.low = QI_IEC_TYPE; | ||
595 | desc.high = 0; | ||
596 | |||
597 | qi_submit_sync(&desc, iommu); | ||
598 | } | ||
599 | |||
600 | /* | ||
601 | * Enable Queued Invalidation interface. This is a must to support | ||
602 | * interrupt-remapping. Also used by DMA-remapping, which replaces | ||
603 | * register based IOTLB invalidation. | ||
604 | */ | ||
605 | int dmar_enable_qi(struct intel_iommu *iommu) | ||
606 | { | ||
607 | u32 cmd, sts; | ||
608 | unsigned long flags; | ||
609 | struct q_inval *qi; | ||
610 | |||
611 | if (!ecap_qis(iommu->ecap)) | ||
612 | return -ENOENT; | ||
613 | |||
614 | /* | ||
615 | * queued invalidation is already setup and enabled. | ||
616 | */ | ||
617 | if (iommu->qi) | ||
618 | return 0; | ||
619 | |||
620 | iommu->qi = kmalloc(sizeof(*qi), GFP_KERNEL); | ||
621 | if (!iommu->qi) | ||
622 | return -ENOMEM; | ||
623 | |||
624 | qi = iommu->qi; | ||
625 | |||
626 | qi->desc = (void *)(get_zeroed_page(GFP_KERNEL)); | ||
627 | if (!qi->desc) { | ||
628 | kfree(qi); | ||
629 | iommu->qi = 0; | ||
630 | return -ENOMEM; | ||
631 | } | ||
632 | |||
633 | qi->desc_status = kmalloc(QI_LENGTH * sizeof(int), GFP_KERNEL); | ||
634 | if (!qi->desc_status) { | ||
635 | free_page((unsigned long) qi->desc); | ||
636 | kfree(qi); | ||
637 | iommu->qi = 0; | ||
638 | return -ENOMEM; | ||
639 | } | ||
640 | |||
641 | qi->free_head = qi->free_tail = 0; | ||
642 | qi->free_cnt = QI_LENGTH; | ||
643 | |||
644 | spin_lock_init(&qi->q_lock); | ||
645 | |||
646 | spin_lock_irqsave(&iommu->register_lock, flags); | ||
647 | /* write zero to the tail reg */ | ||
648 | writel(0, iommu->reg + DMAR_IQT_REG); | ||
649 | |||
650 | dmar_writeq(iommu->reg + DMAR_IQA_REG, virt_to_phys(qi->desc)); | ||
651 | |||
652 | cmd = iommu->gcmd | DMA_GCMD_QIE; | ||
653 | iommu->gcmd |= DMA_GCMD_QIE; | ||
654 | writel(cmd, iommu->reg + DMAR_GCMD_REG); | ||
655 | |||
656 | /* Make sure hardware complete it */ | ||
657 | IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl, (sts & DMA_GSTS_QIES), sts); | ||
658 | spin_unlock_irqrestore(&iommu->register_lock, flags); | ||
659 | |||
660 | return 0; | ||
661 | } | ||
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index fb701d9dd8c0..347bf2e47168 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c | |||
@@ -181,13 +181,6 @@ void free_iova_mem(struct iova *iova) | |||
181 | kmem_cache_free(iommu_iova_cache, iova); | 181 | kmem_cache_free(iommu_iova_cache, iova); |
182 | } | 182 | } |
183 | 183 | ||
184 | static inline void __iommu_flush_cache( | ||
185 | struct intel_iommu *iommu, void *addr, int size) | ||
186 | { | ||
187 | if (!ecap_coherent(iommu->ecap)) | ||
188 | clflush_cache_range(addr, size); | ||
189 | } | ||
190 | |||
191 | /* Gets context entry for a given bus and devfn */ | 184 | /* Gets context entry for a given bus and devfn */ |
192 | static struct context_entry * device_to_context_entry(struct intel_iommu *iommu, | 185 | static struct context_entry * device_to_context_entry(struct intel_iommu *iommu, |
193 | u8 bus, u8 devfn) | 186 | u8 bus, u8 devfn) |
diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h index 3a650e8cba33..2983ce895353 100644 --- a/drivers/pci/intel-iommu.h +++ b/drivers/pci/intel-iommu.h | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/sysdev.h> | 27 | #include <linux/sysdev.h> |
28 | #include "iova.h" | 28 | #include "iova.h" |
29 | #include <linux/io.h> | 29 | #include <linux/io.h> |
30 | #include <asm/cacheflush.h> | ||
30 | #include "dma_remapping.h" | 31 | #include "dma_remapping.h" |
31 | 32 | ||
32 | /* | 33 | /* |
@@ -51,6 +52,10 @@ | |||
51 | #define DMAR_PLMLIMIT_REG 0x6c /* PMRR low limit */ | 52 | #define DMAR_PLMLIMIT_REG 0x6c /* PMRR low limit */ |
52 | #define DMAR_PHMBASE_REG 0x70 /* pmrr high base addr */ | 53 | #define DMAR_PHMBASE_REG 0x70 /* pmrr high base addr */ |
53 | #define DMAR_PHMLIMIT_REG 0x78 /* pmrr high limit */ | 54 | #define DMAR_PHMLIMIT_REG 0x78 /* pmrr high limit */ |
55 | #define DMAR_IQH_REG 0x80 /* Invalidation queue head register */ | ||
56 | #define DMAR_IQT_REG 0x88 /* Invalidation queue tail register */ | ||
57 | #define DMAR_IQA_REG 0x90 /* Invalidation queue addr register */ | ||
58 | #define DMAR_ICS_REG 0x98 /* Invalidation complete status register */ | ||
54 | 59 | ||
55 | #define OFFSET_STRIDE (9) | 60 | #define OFFSET_STRIDE (9) |
56 | /* | 61 | /* |
@@ -114,6 +119,7 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) | |||
114 | #define ecap_max_iotlb_offset(e) \ | 119 | #define ecap_max_iotlb_offset(e) \ |
115 | (ecap_iotlb_offset(e) + ecap_niotlb_iunits(e) * 16) | 120 | (ecap_iotlb_offset(e) + ecap_niotlb_iunits(e) * 16) |
116 | #define ecap_coherent(e) ((e) & 0x1) | 121 | #define ecap_coherent(e) ((e) & 0x1) |
122 | #define ecap_qis(e) ((e) & 0x2) | ||
117 | #define ecap_eim_support(e) ((e >> 4) & 0x1) | 123 | #define ecap_eim_support(e) ((e >> 4) & 0x1) |
118 | #define ecap_ir_support(e) ((e >> 3) & 0x1) | 124 | #define ecap_ir_support(e) ((e >> 3) & 0x1) |
119 | 125 | ||
@@ -131,6 +137,17 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) | |||
131 | #define DMA_TLB_IH_NONLEAF (((u64)1) << 6) | 137 | #define DMA_TLB_IH_NONLEAF (((u64)1) << 6) |
132 | #define DMA_TLB_MAX_SIZE (0x3f) | 138 | #define DMA_TLB_MAX_SIZE (0x3f) |
133 | 139 | ||
140 | /* INVALID_DESC */ | ||
141 | #define DMA_ID_TLB_GLOBAL_FLUSH (((u64)1) << 3) | ||
142 | #define DMA_ID_TLB_DSI_FLUSH (((u64)2) << 3) | ||
143 | #define DMA_ID_TLB_PSI_FLUSH (((u64)3) << 3) | ||
144 | #define DMA_ID_TLB_READ_DRAIN (((u64)1) << 7) | ||
145 | #define DMA_ID_TLB_WRITE_DRAIN (((u64)1) << 6) | ||
146 | #define DMA_ID_TLB_DID(id) (((u64)((id & 0xffff) << 16))) | ||
147 | #define DMA_ID_TLB_IH_NONLEAF (((u64)1) << 6) | ||
148 | #define DMA_ID_TLB_ADDR(addr) (addr) | ||
149 | #define DMA_ID_TLB_ADDR_MASK(mask) (mask) | ||
150 | |||
134 | /* PMEN_REG */ | 151 | /* PMEN_REG */ |
135 | #define DMA_PMEN_EPM (((u32)1)<<31) | 152 | #define DMA_PMEN_EPM (((u32)1)<<31) |
136 | #define DMA_PMEN_PRS (((u32)1)<<0) | 153 | #define DMA_PMEN_PRS (((u32)1)<<0) |
@@ -140,6 +157,7 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) | |||
140 | #define DMA_GCMD_SRTP (((u32)1) << 30) | 157 | #define DMA_GCMD_SRTP (((u32)1) << 30) |
141 | #define DMA_GCMD_SFL (((u32)1) << 29) | 158 | #define DMA_GCMD_SFL (((u32)1) << 29) |
142 | #define DMA_GCMD_EAFL (((u32)1) << 28) | 159 | #define DMA_GCMD_EAFL (((u32)1) << 28) |
160 | #define DMA_GCMD_QIE (((u32)1) << 26) | ||
143 | #define DMA_GCMD_WBF (((u32)1) << 27) | 161 | #define DMA_GCMD_WBF (((u32)1) << 27) |
144 | 162 | ||
145 | /* GSTS_REG */ | 163 | /* GSTS_REG */ |
@@ -147,6 +165,7 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) | |||
147 | #define DMA_GSTS_RTPS (((u32)1) << 30) | 165 | #define DMA_GSTS_RTPS (((u32)1) << 30) |
148 | #define DMA_GSTS_FLS (((u32)1) << 29) | 166 | #define DMA_GSTS_FLS (((u32)1) << 29) |
149 | #define DMA_GSTS_AFLS (((u32)1) << 28) | 167 | #define DMA_GSTS_AFLS (((u32)1) << 28) |
168 | #define DMA_GSTS_QIES (((u32)1) << 26) | ||
150 | #define DMA_GSTS_WBFS (((u32)1) << 27) | 169 | #define DMA_GSTS_WBFS (((u32)1) << 27) |
151 | 170 | ||
152 | /* CCMD_REG */ | 171 | /* CCMD_REG */ |
@@ -192,6 +211,40 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) | |||
192 | }\ | 211 | }\ |
193 | } | 212 | } |
194 | 213 | ||
214 | #define QI_LENGTH 256 /* queue length */ | ||
215 | |||
216 | enum { | ||
217 | QI_FREE, | ||
218 | QI_IN_USE, | ||
219 | QI_DONE | ||
220 | }; | ||
221 | |||
222 | #define QI_CC_TYPE 0x1 | ||
223 | #define QI_IOTLB_TYPE 0x2 | ||
224 | #define QI_DIOTLB_TYPE 0x3 | ||
225 | #define QI_IEC_TYPE 0x4 | ||
226 | #define QI_IWD_TYPE 0x5 | ||
227 | |||
228 | #define QI_IEC_SELECTIVE (((u64)1) << 4) | ||
229 | #define QI_IEC_IIDEX(idx) (((u64)(idx & 0xffff) << 32)) | ||
230 | #define QI_IEC_IM(m) (((u64)(m & 0x1f) << 27)) | ||
231 | |||
232 | #define QI_IWD_STATUS_DATA(d) (((u64)d) << 32) | ||
233 | #define QI_IWD_STATUS_WRITE (((u64)1) << 5) | ||
234 | |||
235 | struct qi_desc { | ||
236 | u64 low, high; | ||
237 | }; | ||
238 | |||
239 | struct q_inval { | ||
240 | spinlock_t q_lock; | ||
241 | struct qi_desc *desc; /* invalidation queue */ | ||
242 | int *desc_status; /* desc status */ | ||
243 | int free_head; /* first free entry */ | ||
244 | int free_tail; /* last free entry */ | ||
245 | int free_cnt; | ||
246 | }; | ||
247 | |||
195 | struct intel_iommu { | 248 | struct intel_iommu { |
196 | void __iomem *reg; /* Pointer to hardware regs, virtual addr */ | 249 | void __iomem *reg; /* Pointer to hardware regs, virtual addr */ |
197 | u64 cap; | 250 | u64 cap; |
@@ -212,8 +265,16 @@ struct intel_iommu { | |||
212 | struct msi_msg saved_msg; | 265 | struct msi_msg saved_msg; |
213 | struct sys_device sysdev; | 266 | struct sys_device sysdev; |
214 | #endif | 267 | #endif |
268 | struct q_inval *qi; /* Queued invalidation info */ | ||
215 | }; | 269 | }; |
216 | 270 | ||
271 | static inline void __iommu_flush_cache( | ||
272 | struct intel_iommu *iommu, void *addr, int size) | ||
273 | { | ||
274 | if (!ecap_coherent(iommu->ecap)) | ||
275 | clflush_cache_range(addr, size); | ||
276 | } | ||
277 | |||
217 | extern struct dmar_drhd_unit * dmar_find_matched_drhd_unit(struct pci_dev *dev); | 278 | extern struct dmar_drhd_unit * dmar_find_matched_drhd_unit(struct pci_dev *dev); |
218 | 279 | ||
219 | extern int alloc_iommu(struct dmar_drhd_unit *drhd); | 280 | extern int alloc_iommu(struct dmar_drhd_unit *drhd); |