@@ -277,9 +277,22 @@ static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value,
VTDIOTLBPageInvInfo *info = (VTDIOTLBPageInvInfo *)user_data;
uint64_t gfn = (info->addr >> VTD_PAGE_SHIFT_4K) & info->mask;
uint64_t gfn_tlb = (info->addr & entry->mask) >> VTD_PAGE_SHIFT_4K;
- return (entry->domain_id == info->domain_id) &&
- (((entry->gfn & info->mask) == gfn) ||
- (entry->gfn == gfn_tlb));
+ return (
+ (entry->domain_id == info->domain_id) &&
+ (info->pasid == entry->pasid)
+ ) && (
+ ((entry->gfn & info->mask) == gfn) ||
+ (entry->gfn == gfn_tlb)
+ );
+}
+
+static gboolean vtd_hash_remove_by_pasid(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ VTDIOTLBEntry *entry = (VTDIOTLBEntry *)value;
+ VTDIOTLBPasidEntryInvInfo *info = (VTDIOTLBPasidEntryInvInfo *)user_data;
+ return ((entry->domain_id == info->domain_id) &&
+ (info->pasid == entry->pasid));
}
/* Reset all the gen of VTDAddressSpace to zero and set the gen of
@@ -1287,8 +1300,10 @@ static int vtd_iova_to_pte_sl(IntelIOMMUState *s, VTDContextEntry *ce,
if (ret != 0) {
return ret;
}
+
*reads = (*reads) && (slpte & VTD_SL_R);
*writes = (*writes) && (slpte & VTD_SL_W);
+
if ((slpte & access_right_check) != access_right_check) {
error_report_once("%s: detected slpte permission error "
"(iova=0x%" PRIx64 ", level=0x%" PRIx32 ", "
@@ -2484,23 +2499,61 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s,
}
}
+static VTDIOTLBPageInvInfo vtd_build_tlb_page_inv_info(uint16_t domain_id,
+ hwaddr addr, uint8_t am,
+ uint32_t pasid)
+{
+ assert(am <= VTD_MAMV);
+ VTDIOTLBPageInvInfo info = {
+ .domain_id = domain_id,
+ .addr = addr,
+ .mask = ~((1ULL << am) - 1),
+ .pasid = pasid
+ };
+ return info;
+}
+
static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id,
hwaddr addr, uint8_t am)
{
- VTDIOTLBPageInvInfo info;
+ VTDIOTLBPageInvInfo info = vtd_build_tlb_page_inv_info(domain_id, addr,
+ am, PCI_NO_PASID);
trace_vtd_inv_desc_iotlb_pages(domain_id, addr, am);
- assert(am <= VTD_MAMV);
- info.domain_id = domain_id;
- info.addr = addr;
- info.mask = ~((1 << am) - 1);
vtd_iommu_lock(s);
g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info);
vtd_iommu_unlock(s);
+
vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am, PCI_NO_PASID);
}
+static void vtd_pasid_based_iotlb_page_invalidate(IntelIOMMUState *s,
+ uint16_t domain_id,
+ hwaddr addr,
+ uint8_t am, uint32_t pasid)
+{
+ VTDIOTLBPageInvInfo info = vtd_build_tlb_page_inv_info(domain_id, addr,
+ am, pasid);
+ vtd_iommu_lock(s);
+ g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info);
+ vtd_iommu_unlock(s);
+}
+
+static void vtd_pasid_based_iotlb_invalidate(IntelIOMMUState *s,
+ uint16_t domain_id,
+ uint32_t pasid)
+{
+ assert(pasid != PCI_NO_PASID);
+ VTDIOTLBPasidEntryInvInfo info = {
+ .domain_id = domain_id,
+ .pasid = pasid
+ };
+ vtd_iommu_lock(s);
+ g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_pasid, &info);
+ vtd_iommu_unlock(s);
+}
+
/* Flush IOTLB
* Returns the IOTLB Actual Invalidation Granularity.
* @val: the content of the IOTLB_REG
@@ -2759,7 +2812,7 @@ static bool vtd_get_inv_desc(IntelIOMMUState *s,
static bool vtd_process_wait_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc)
{
if ((inv_desc->hi & VTD_INV_DESC_WAIT_RSVD_HI) ||
- (inv_desc->lo & VTD_INV_DESC_WAIT_RSVD_LO)) {
+ (inv_desc->lo & VTD_INV_DESC_WAIT_RSVD_LO(s->ecap))) {
error_report_once("%s: invalid wait desc: hi=%"PRIx64", lo=%"PRIx64
" (reserved nonzero)", __func__, inv_desc->hi,
inv_desc->lo);
@@ -2785,6 +2838,11 @@ static bool vtd_process_wait_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc)
} else if (inv_desc->lo & VTD_INV_DESC_WAIT_IF) {
/* Interrupt flag */
vtd_generate_completion_event(s);
+ } else if (inv_desc->lo & VTD_INV_DESC_WAIT_FN) {
+ /*
+ * SW = 0, IF = 0, FN = 1
+ * Nothing to do as we process the events sequentially
+ */
} else {
error_report_once("%s: invalid wait desc: hi=%"PRIx64", lo=%"PRIx64
" (unknown type)", __func__, inv_desc->hi,
@@ -2957,6 +3015,54 @@ done:
return true;
}
+static bool vtd_process_piotlb_desc(IntelIOMMUState *s,
+ VTDInvDesc *inv_desc)
+{
+ uint32_t pasid;
+ uint16_t domain_id;
+ hwaddr addr;
+ uint8_t am;
+
+ if ((inv_desc->lo & VTD_INV_DESC_IOTLB_PASID_RSVD_LO) ||
+ (inv_desc->hi & VTD_INV_DESC_IOTLB_PASID_RSVD_HI)) {
+ error_report_once("%s: invalid piotlb inv desc: hi=0x%"PRIx64
+ ", lo=0x%"PRIx64" (reserved bits unzero)",
+ __func__, inv_desc->hi, inv_desc->lo);
+ return false;
+ }
+
+ domain_id = VTD_INV_DESC_IOTLB_DID(inv_desc->lo);
+ pasid = VTD_INV_DESC_IOTLB_PASID(inv_desc->lo);
+ addr = VTD_INV_DESC_IOTLB_ADDR(inv_desc->hi);
+ am = VTD_INV_DESC_IOTLB_AM(inv_desc->hi);
+
+ switch (inv_desc->lo & VTD_INV_DESC_IOTLB_G) {
+ case VTD_INV_DESC_IOTLB_PASID_PASID:
+ vtd_pasid_based_iotlb_invalidate(s, domain_id, pasid);
+ break;
+
+ case VTD_INV_DESC_IOTLB_PASID_PAGE:
+ if (am > VTD_MAMV) {
+ error_report_once("%s: invalid piotlb inv desc: hi=0x%"PRIx64
+ ", lo=0x%"PRIx64" (am=%u > VTD_MAMV=%u)",
+ __func__, inv_desc->hi, inv_desc->lo,
+ am, (unsigned)VTD_MAMV);
+ return false;
+ }
+ vtd_pasid_based_iotlb_page_invalidate(s, domain_id, addr, am, pasid);
+ break;
+
+ default:
+ error_report_once("%s: invalid piotlb inv desc: hi=0x%"PRIx64
+ ", lo=0x%"PRIx64" (type mismatch: 0x%llx)",
+ __func__, inv_desc->hi, inv_desc->lo,
+ inv_desc->lo & VTD_INV_DESC_IOTLB_G);
+ return false;
+ }
+
+ return true;
+}
+
static bool vtd_process_inv_desc(IntelIOMMUState *s)
{
VTDInvDesc inv_desc;
@@ -2988,7 +3094,7 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s)
break;
/*
- * TODO: the entity of below two cases will be implemented in future series.
+ * TODO: the entity of below case will be implemented in future series.
* To make guest (which integrates scalable mode support patch set in
* iommu driver) work, just return true is enough so far.
*/
@@ -2996,6 +3102,10 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s)
break;
case VTD_INV_DESC_PIOTLB:
+ trace_vtd_inv_desc("piotlb", inv_desc.hi, inv_desc.lo);
+ if (!vtd_process_piotlb_desc(s, &inv_desc)) {
+ return false;
+ }
break;
case VTD_INV_DESC_WAIT:
@@ -193,6 +193,7 @@
#define VTD_ECAP_MHMV (15ULL << 20)
#define VTD_ECAP_SRS (1ULL << 31)
#define VTD_ECAP_PASID (1ULL << 40)
+#define VTD_ECAP_PDS (1ULL << 42)
#define VTD_ECAP_SMTS (1ULL << 43)
#define VTD_ECAP_SLTS (1ULL << 46)
#define VTD_ECAP_FLTS (1ULL << 47)
@@ -386,12 +387,13 @@ typedef union VTDInvDesc VTDInvDesc;
#define VTD_INV_DESC_NONE 0 /* Not an Invalidate Descriptor */
/* Masks for Invalidation Wait Descriptor*/
-#define VTD_INV_DESC_WAIT_SW (1ULL << 5)
-#define VTD_INV_DESC_WAIT_IF (1ULL << 4)
-#define VTD_INV_DESC_WAIT_FN (1ULL << 6)
-#define VTD_INV_DESC_WAIT_DATA_SHIFT 32
-#define VTD_INV_DESC_WAIT_RSVD_LO 0Xffffff80ULL
-#define VTD_INV_DESC_WAIT_RSVD_HI 3ULL
+#define VTD_INV_DESC_WAIT_SW (1ULL << 5)
+#define VTD_INV_DESC_WAIT_IF (1ULL << 4)
+#define VTD_INV_DESC_WAIT_FN (1ULL << 6)
+#define VTD_INV_DESC_WAIT_DATA_SHIFT 32
+#define VTD_INV_DESC_WAIT_RSVD_LO(ecap) (0xffffff00ULL | \
+ ((ecap & VTD_ECAP_PDS) ? 0 : (1 << 7)))
+#define VTD_INV_DESC_WAIT_RSVD_HI 3ULL
/* Masks for Context-cache Invalidation Descriptor */
#define VTD_INV_DESC_CC_G (3ULL << 4)
@@ -404,20 +406,20 @@ typedef union VTDInvDesc VTDInvDesc;
#define VTD_INV_DESC_CC_RSVD 0xfffc00000000ffc0ULL
/* Masks for IOTLB Invalidate Descriptor */
-#define VTD_INV_DESC_IOTLB_G (3ULL << 4)
-#define VTD_INV_DESC_IOTLB_GLOBAL (1ULL << 4)
-#define VTD_INV_DESC_IOTLB_DOMAIN (2ULL << 4)
-#define VTD_INV_DESC_IOTLB_PAGE (3ULL << 4)
-#define VTD_INV_DESC_IOTLB_DID(val) (((val) >> 16) & VTD_DOMAIN_ID_MASK)
-#define VTD_INV_DESC_IOTLB_ADDR(val) ((val) & ~0xfffULL)
-#define VTD_INV_DESC_IOTLB_AM(val) ((val) & 0x3fULL)
-#define VTD_INV_DESC_IOTLB_RSVD_LO 0xffffffff0000ff00ULL
-#define VTD_INV_DESC_IOTLB_RSVD_HI 0xf80ULL
-#define VTD_INV_DESC_IOTLB_PASID_PASID (2ULL << 4)
-#define VTD_INV_DESC_IOTLB_PASID_PAGE (3ULL << 4)
-#define VTD_INV_DESC_IOTLB_PASID(val) (((val) >> 32) & VTD_PASID_ID_MASK)
-#define VTD_INV_DESC_IOTLB_PASID_RSVD_LO 0xfff00000000001c0ULL
-#define VTD_INV_DESC_IOTLB_PASID_RSVD_HI 0xf80ULL
+#define VTD_INV_DESC_IOTLB_G (3ULL << 4)
+#define VTD_INV_DESC_IOTLB_GLOBAL (1ULL << 4)
+#define VTD_INV_DESC_IOTLB_DOMAIN (2ULL << 4)
+#define VTD_INV_DESC_IOTLB_PAGE (3ULL << 4)
+#define VTD_INV_DESC_IOTLB_DID(val) (((val) >> 16) & VTD_DOMAIN_ID_MASK)
+#define VTD_INV_DESC_IOTLB_ADDR(val) ((val) & ~0xfffULL)
+#define VTD_INV_DESC_IOTLB_AM(val) ((val) & 0x3fULL)
+#define VTD_INV_DESC_IOTLB_RSVD_LO 0xffffffff0000ff00ULL
+#define VTD_INV_DESC_IOTLB_RSVD_HI 0xf80ULL
+#define VTD_INV_DESC_IOTLB_PASID_PASID (2ULL << 4)
+#define VTD_INV_DESC_IOTLB_PASID_PAGE (3ULL << 4)
+#define VTD_INV_DESC_IOTLB_PASID(val) (((val) >> 32) & VTD_PASID_ID_MASK)
+#define VTD_INV_DESC_IOTLB_PASID_RSVD_LO 0xfff000000000ffc0ULL
+#define VTD_INV_DESC_IOTLB_PASID_RSVD_HI 0xf80ULL
/* Mask for Device IOTLB Invalidate Descriptor */
#define VTD_INV_DESC_DEVICE_IOTLB_ADDR(val) ((val) & 0xfffffffffffff000ULL)
@@ -471,10 +473,17 @@ struct VTDIOTLBPageInvInfo {
uint16_t domain_id;
uint32_t pasid;
uint64_t addr;
- uint8_t mask;
+ uint64_t mask;
};
typedef struct VTDIOTLBPageInvInfo VTDIOTLBPageInvInfo;
+/* Information about PASID-selective IOTLB invalidate */
+struct VTDIOTLBPasidEntryInvInfo {
+ uint16_t domain_id;
+ uint32_t pasid;
+};
+typedef struct VTDIOTLBPasidEntryInvInfo VTDIOTLBPasidEntryInvInfo;
+
/* Pagesize of VTD paging structures, including root and context tables */
#define VTD_PAGE_SHIFT 12
#define VTD_PAGE_SIZE (1ULL << VTD_PAGE_SHIFT)
Signed-off-by: Clément Mathieu--Drif <clement.mathieu--drif@eviden.com> --- hw/i386/intel_iommu.c | 130 ++++++++++++++++++++++++++++++--- hw/i386/intel_iommu_internal.h | 51 +++++++------ 2 files changed, 150 insertions(+), 31 deletions(-)