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);