iommu: add support for iotlb shootdowns and write-buffer flushing
* Add fields to struct iommu which are set during initialization
* Add the necessary functions
* Will also need another function to perform global iotlb flushes
* Also look into Queue Invalidation (QI) for device IOTLBs. Currently we
do not allow device IOTLBs to be populated. For device IOTLBs we need
to set the TRANS_TYPE to 0x01.
Signed-off-by: Aditya Basu <mitthu@google.com>
Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
diff --git a/kern/arch/x86/intel-iommu.h b/kern/arch/x86/intel-iommu.h
index 008adf8..4706fdc 100644
--- a/kern/arch/x86/intel-iommu.h
+++ b/kern/arch/x86/intel-iommu.h
@@ -53,7 +53,11 @@
TAILQ_ENTRY(iommu) iommu_link;
struct proc_list procs; // unused
bool supported;
+ bool device_iotlb;
+ bool rwbf;
+ uint16_t iotlb_cmd_offset;
+ uint16_t iotlb_addr_offset;
void __iomem *regio;
uint64_t rba; /* for unique assertion */
uint64_t num_assigned_devs;
diff --git a/kern/drivers/dev/iommu.c b/kern/drivers/dev/iommu.c
index a761795..9ae50fb 100644
--- a/kern/drivers/dev/iommu.c
+++ b/kern/drivers/dev/iommu.c
@@ -56,6 +56,46 @@
{"power", {Qpower, 0, QTFILE}, 0, 0755},
};
+/* this is might be necessary when updating mapping structures: context-cache,
+ * IOTLB or IEC. */
+static inline void write_buffer_flush(struct iommu *iommu)
+{
+ uint32_t cmd, status;
+
+ if (!iommu->rwbf)
+ return;
+
+ cmd = read32(iommu->regio + DMAR_GCMD_REG) | DMA_GCMD_WBF;
+ write32(cmd, iommu->regio + DMAR_GCMD_REG);
+
+ /* read status */
+ do {
+ status = read32(iommu->regio + DMAR_GSTS_REG);
+ } while (status & DMA_GSTS_WBFS);
+}
+
+/* this is necessary when caching mode is supported.
+ * ASSUMES: No pending flush requests. This is a problem only if other function
+ * is used to perform the flush. */
+static inline void iotlb_flush(struct iommu *iommu, uint16_t did)
+{
+ uint64_t cmd, status;
+
+ cmd = 0x0
+ | DMA_TLB_IVT /* issue the flush command */
+ | DMA_TLB_DSI_FLUSH /* DID specific shootdown */
+ | DMA_TLB_READ_DRAIN
+ | DMA_TLB_WRITE_DRAIN
+ | DMA_TLB_DID(did);
+ write64(cmd, iommu->regio + iommu->iotlb_cmd_offset);
+
+ /* read status */
+ do {
+ status = read64(iommu->regio + iommu->iotlb_cmd_offset);
+ status >>= 63; /* bit 64 (IVT): gets cleared on completion */
+ } while (status);
+}
+
static inline struct root_entry *get_root_entry(physaddr_t paddr)
{
return (struct root_entry *) KADDR(paddr);