diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 431853386db8..e5a03f84d2f5 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -233,10 +233,38 @@ struct context_entry { u64 hi; }; -static inline bool context_present(struct context_entry *context) +static inline void context_clear_pasid_enable(struct context_entry *context) +{ + context->lo &= ~(1ULL << 11); +} + +static inline bool context_pasid_enabled(struct context_entry *context) +{ + return !!(context->lo & (1ULL << 11)); +} + +static inline void context_set_copied(struct context_entry *context) +{ + context->hi |= (1ull << 3); +} + +static inline bool context_copied(struct context_entry *context) +{ + return !!(context->hi & (1ULL << 3)); +} + +static inline bool __context_present(struct context_entry *context) { return (context->lo & 1); } + +static inline bool context_present(struct context_entry *context) +{ + return context_pasid_enabled(context) ? + __context_present(context) : + __context_present(context) && !context_copied(context); +} + static inline void context_set_present(struct context_entry *context) { context->lo |= 1; @@ -1861,6 +1889,8 @@ static int domain_context_mapping_one(struct dmar_domain *domain, return 0; } + context_clear_entry(context); + id = domain->id; pgd = domain->pgd; @@ -2859,13 +2889,32 @@ static int copy_context_table(struct intel_iommu *iommu, /* Now copy the context entry */ ce = old_ce[idx]; - if (!context_present(&ce)) + if (!__context_present(&ce)) continue; did = context_domain_id(&ce); if (did >= 0 && did < cap_ndoms(iommu->cap)) set_bit(did, iommu->domain_ids); + /* + * We need a marker for copied context entries. This + * marker needs to work for the old format as well as + * for extended context entries. + * + * Bit 67 of the context entry is used. In the old + * format this bit is available to software, in the + * extended format it is the PGE bit, but PGE is ignored + * by HW if PASIDs are disabled (and thus still + * available). + * + * So disable PASIDs first and then mark the entry + * copied. This means that we don't copy PASID + * translations from the old kernel, but this is fine as + * faults there are not fatal. + */ + context_clear_pasid_enable(&ce); + context_set_copied(&ce); + new_ce[idx] = ce; }