@@ -3102,15 +3102,137 @@ static bool vtd_process_pasid_desc(IntelIOMMUState *s,
return (ret == 0) ? true : false;
}
+static void vtd_invalidate_piotlb(IntelIOMMUState *s, VTDBus *vtd_bus,
+ int devfn, DualIOMMUStage1Cache *stage1_cache)
+{
+ VTDIOMMUContext *vtd_icx;
+ vtd_icx = vtd_bus->dev_icx[devfn];
+ if (!vtd_icx) {
+ return;
+ }
+ if (ds_iommu_flush_stage1_cache(vtd_icx->dsi_obj, stage1_cache)) {
+ error_report("Cache flush failed");
+ }
+}
+
+static inline bool vtd_pasid_cache_valid(
+ VTDPASIDAddressSpace *vtd_pasid_as)
+{
+ return (vtd_pasid_as->iommu_state->pasid_cache_gen &&
+ (vtd_pasid_as->iommu_state->pasid_cache_gen
+ == vtd_pasid_as->pasid_cache_entry.pasid_cache_gen));
+}
+
+/**
+ * This function is a loop function for the s->vtd_pasid_as
+ * list with VTDPIOTLBInvInfo as execution filter. It propagates
+ * the piotlb invalidation to host. Caller of this function
+ * should hold iommu_lock.
+ */
+static void vtd_flush_pasid_iotlb(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ VTDPIOTLBInvInfo *piotlb_info = user_data;
+ VTDPASIDAddressSpace *vtd_pasid_as = value;
+ uint16_t did;
+
+ /*
+ * Needs to check whether the pasid entry cache stored in
+ * vtd_pasid_as is valid or not. "invalid" means the pasid
+ * cache has been flushed, thus host should have done piotlb
+ * invalidation together with a pasid cache invalidation, so
+ * no need to pass down piotlb invalidation to host for better
+ * performance. Only when pasid entry cache is "valid", should
+ * a piotlb invalidation be propagated to host since it means
+ * guest just modified a mapping in its page table.
+ */
+ if (!vtd_pasid_cache_valid(vtd_pasid_as)) {
+ return;
+ }
+
+ did = vtd_pe_get_domain_id(
+ &(vtd_pasid_as->pasid_cache_entry.pasid_entry));
+
+ if ((piotlb_info->domain_id == did) &&
+ (piotlb_info->pasid == vtd_pasid_as->pasid)) {
+ vtd_invalidate_piotlb(vtd_pasid_as->iommu_state,
+ vtd_pasid_as->vtd_bus,
+ vtd_pasid_as->devfn,
+ piotlb_info->stage1_cache);
+ }
+
+ /*
+ * TODO: needs to add QEMU piotlb flush when QEMU piotlb
+ * infrastructure is ready. For now, it is enough for passthru
+ * devices.
+ */
+}
+
static void vtd_piotlb_pasid_invalidate(IntelIOMMUState *s,
uint16_t domain_id,
uint32_t pasid)
{
+ VTDPIOTLBInvInfo piotlb_info;
+ struct iommu_cache_invalidate_info *cache_info;
+ DualIOMMUStage1Cache stage1_cache;
+
+ stage1_cache.pasid = pasid;
+
+ cache_info = &stage1_cache.cache_info;
+ cache_info->version = IOMMU_UAPI_VERSION;
+ cache_info->cache = IOMMU_CACHE_INV_TYPE_IOTLB;
+ cache_info->granularity = IOMMU_INV_GRANU_PASID;
+ cache_info->pasid_info.pasid = pasid;
+ cache_info->pasid_info.flags = IOMMU_INV_PASID_FLAGS_PASID;
+
+ piotlb_info.domain_id = domain_id;
+ piotlb_info.pasid = pasid;
+ piotlb_info.stage1_cache = &stage1_cache;
+
+ vtd_iommu_lock(s);
+ /*
+ * Here loops all the vtd_pasid_as instances in s->vtd_pasid_as
+ * to find out the affected devices since piotlb invalidation
+ * should check pasid cache per architecture point of view.
+ */
+ g_hash_table_foreach(s->vtd_pasid_as,
+ vtd_flush_pasid_iotlb, &piotlb_info);
+ vtd_iommu_unlock(s);
}
static void vtd_piotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id,
uint32_t pasid, hwaddr addr, uint8_t am, bool ih)
{
+ VTDPIOTLBInvInfo piotlb_info;
+ struct iommu_cache_invalidate_info *cache_info;
+ DualIOMMUStage1Cache stage1_cache;
+
+ stage1_cache.pasid = pasid;
+
+ cache_info = &stage1_cache.cache_info;
+ cache_info->version = IOMMU_UAPI_VERSION;
+ cache_info->cache = IOMMU_CACHE_INV_TYPE_IOTLB;
+ cache_info->granularity = IOMMU_INV_GRANU_ADDR;
+ cache_info->addr_info.flags = IOMMU_INV_ADDR_FLAGS_PASID;
+ cache_info->addr_info.flags |= ih ? IOMMU_INV_ADDR_FLAGS_LEAF : 0;
+ cache_info->addr_info.pasid = pasid;
+ cache_info->addr_info.addr = addr;
+ cache_info->addr_info.granule_size = 1 << (12 + am);
+ cache_info->addr_info.nb_granules = 1;
+
+ piotlb_info.domain_id = domain_id;
+ piotlb_info.pasid = pasid;
+ piotlb_info.stage1_cache = &stage1_cache;
+
+ vtd_iommu_lock(s);
+ /*
+ * Here loops all the vtd_pasid_as instances in s->vtd_pasid_as
+ * to find out the affected devices since piotlb invalidation
+ * should check pasid cache per architecture point of view.
+ */
+ g_hash_table_foreach(s->vtd_pasid_as,
+ vtd_flush_pasid_iotlb, &piotlb_info);
+ vtd_iommu_unlock(s);
}
static bool vtd_process_piotlb_desc(IntelIOMMUState *s,
@@ -530,6 +530,13 @@ struct VTDPASIDCacheInfo {
VTD_PASID_CACHE_DEVSI)
typedef struct VTDPASIDCacheInfo VTDPASIDCacheInfo;
+struct VTDPIOTLBInvInfo {
+ uint16_t domain_id;
+ uint32_t pasid;
+ DualIOMMUStage1Cache *stage1_cache;
+};
+typedef struct VTDPIOTLBInvInfo VTDPIOTLBInvInfo;
+
/* Masks for struct VTDRootEntry */
#define VTD_ROOT_ENTRY_P 1ULL
#define VTD_ROOT_ENTRY_CTP (~0xfffULL)