Signed-off-by: Clément Mathieu--Drif <clement.mathieu--d...@eviden.com> --- hw/i386/intel_iommu.c | 130 ++++++++++++++++++++++++++++++--- hw/i386/intel_iommu_internal.h | 51 +++++++------ 2 files changed, 150 insertions(+), 31 deletions(-)
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index aaac61bf6a..4b54a45107 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -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: diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index ed61979934..4f734ce67b 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -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) -- 2.44.0