Skip to content

Commit 0c5f6c0

Browse files
LuBaolujoergroedel
authored andcommitted
iommu/vt-d: Fix kdump kernels boot failure with scalable mode
The translation table copying code for kdump kernels is currently based on the extended root/context entry formats of ECS mode defined in older VT-d v2.5, and doesn't handle the scalable mode formats. This causes the kexec capture kernel boot failure with DMAR faults if the IOMMU was enabled in scalable mode by the previous kernel. The ECS mode has already been deprecated by the VT-d spec since v3.0 and Intel IOMMU driver doesn't support this mode as there's no real hardware implementation. Hence this converts ECS checking in copying table code into scalable mode. The existing copying code consumes a bit in the context entry as a mark of copied entry. It needs to work for the old format as well as for the extended context entries. As it's hard to find such a common bit for both legacy and scalable mode context entries. This replaces it with a per- IOMMU bitmap. Fixes: 7373a8c ("iommu/vt-d: Setup context and enable RID2PASID support") Cc: [email protected] Reported-by: Jerry Snitselaar <[email protected]> Tested-by: Wen Jin <[email protected]> Signed-off-by: Lu Baolu <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Joerg Roedel <[email protected]>
1 parent 94a568c commit 0c5f6c0

File tree

2 files changed

+50
-59
lines changed

2 files changed

+50
-59
lines changed

drivers/iommu/intel/iommu.c

Lines changed: 44 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -163,38 +163,6 @@ static phys_addr_t root_entry_uctp(struct root_entry *re)
163163
return re->hi & VTD_PAGE_MASK;
164164
}
165165

166-
static inline void context_clear_pasid_enable(struct context_entry *context)
167-
{
168-
context->lo &= ~(1ULL << 11);
169-
}
170-
171-
static inline bool context_pasid_enabled(struct context_entry *context)
172-
{
173-
return !!(context->lo & (1ULL << 11));
174-
}
175-
176-
static inline void context_set_copied(struct context_entry *context)
177-
{
178-
context->hi |= (1ull << 3);
179-
}
180-
181-
static inline bool context_copied(struct context_entry *context)
182-
{
183-
return !!(context->hi & (1ULL << 3));
184-
}
185-
186-
static inline bool __context_present(struct context_entry *context)
187-
{
188-
return (context->lo & 1);
189-
}
190-
191-
bool context_present(struct context_entry *context)
192-
{
193-
return context_pasid_enabled(context) ?
194-
__context_present(context) :
195-
__context_present(context) && !context_copied(context);
196-
}
197-
198166
static inline void context_set_present(struct context_entry *context)
199167
{
200168
context->lo |= 1;
@@ -242,6 +210,26 @@ static inline void context_clear_entry(struct context_entry *context)
242210
context->hi = 0;
243211
}
244212

213+
static inline bool context_copied(struct intel_iommu *iommu, u8 bus, u8 devfn)
214+
{
215+
if (!iommu->copied_tables)
216+
return false;
217+
218+
return test_bit(((long)bus << 8) | devfn, iommu->copied_tables);
219+
}
220+
221+
static inline void
222+
set_context_copied(struct intel_iommu *iommu, u8 bus, u8 devfn)
223+
{
224+
set_bit(((long)bus << 8) | devfn, iommu->copied_tables);
225+
}
226+
227+
static inline void
228+
clear_context_copied(struct intel_iommu *iommu, u8 bus, u8 devfn)
229+
{
230+
clear_bit(((long)bus << 8) | devfn, iommu->copied_tables);
231+
}
232+
245233
/*
246234
* This domain is a statically identity mapping domain.
247235
* 1. This domain creats a static 1:1 mapping to all usable memory.
@@ -578,6 +566,13 @@ struct context_entry *iommu_context_addr(struct intel_iommu *iommu, u8 bus,
578566
struct context_entry *context;
579567
u64 *entry;
580568

569+
/*
570+
* Except that the caller requested to allocate a new entry,
571+
* returning a copied context entry makes no sense.
572+
*/
573+
if (!alloc && context_copied(iommu, bus, devfn))
574+
return NULL;
575+
581576
entry = &root->lo;
582577
if (sm_supported(iommu)) {
583578
if (devfn >= 0x80) {
@@ -1688,6 +1683,11 @@ static void free_dmar_iommu(struct intel_iommu *iommu)
16881683
iommu->domain_ids = NULL;
16891684
}
16901685

1686+
if (iommu->copied_tables) {
1687+
bitmap_free(iommu->copied_tables);
1688+
iommu->copied_tables = NULL;
1689+
}
1690+
16911691
/* free context mapping */
16921692
free_context_table(iommu);
16931693

@@ -1913,7 +1913,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
19131913
goto out_unlock;
19141914

19151915
ret = 0;
1916-
if (context_present(context))
1916+
if (context_present(context) && !context_copied(iommu, bus, devfn))
19171917
goto out_unlock;
19181918

19191919
/*
@@ -1925,7 +1925,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
19251925
* in-flight DMA will exist, and we don't need to worry anymore
19261926
* hereafter.
19271927
*/
1928-
if (context_copied(context)) {
1928+
if (context_copied(iommu, bus, devfn)) {
19291929
u16 did_old = context_domain_id(context);
19301930

19311931
if (did_old < cap_ndoms(iommu->cap)) {
@@ -1936,6 +1936,8 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
19361936
iommu->flush.flush_iotlb(iommu, did_old, 0, 0,
19371937
DMA_TLB_DSI_FLUSH);
19381938
}
1939+
1940+
clear_context_copied(iommu, bus, devfn);
19391941
}
19401942

19411943
context_clear_entry(context);
@@ -2684,32 +2686,14 @@ static int copy_context_table(struct intel_iommu *iommu,
26842686
/* Now copy the context entry */
26852687
memcpy(&ce, old_ce + idx, sizeof(ce));
26862688

2687-
if (!__context_present(&ce))
2689+
if (!context_present(&ce))
26882690
continue;
26892691

26902692
did = context_domain_id(&ce);
26912693
if (did >= 0 && did < cap_ndoms(iommu->cap))
26922694
set_bit(did, iommu->domain_ids);
26932695

2694-
/*
2695-
* We need a marker for copied context entries. This
2696-
* marker needs to work for the old format as well as
2697-
* for extended context entries.
2698-
*
2699-
* Bit 67 of the context entry is used. In the old
2700-
* format this bit is available to software, in the
2701-
* extended format it is the PGE bit, but PGE is ignored
2702-
* by HW if PASIDs are disabled (and thus still
2703-
* available).
2704-
*
2705-
* So disable PASIDs first and then mark the entry
2706-
* copied. This means that we don't copy PASID
2707-
* translations from the old kernel, but this is fine as
2708-
* faults there are not fatal.
2709-
*/
2710-
context_clear_pasid_enable(&ce);
2711-
context_set_copied(&ce);
2712-
2696+
set_context_copied(iommu, bus, devfn);
27132697
new_ce[idx] = ce;
27142698
}
27152699

@@ -2735,8 +2719,8 @@ static int copy_translation_tables(struct intel_iommu *iommu)
27352719
bool new_ext, ext;
27362720

27372721
rtaddr_reg = dmar_readq(iommu->reg + DMAR_RTADDR_REG);
2738-
ext = !!(rtaddr_reg & DMA_RTADDR_RTT);
2739-
new_ext = !!ecap_ecs(iommu->ecap);
2722+
ext = !!(rtaddr_reg & DMA_RTADDR_SMT);
2723+
new_ext = !!sm_supported(iommu);
27402724

27412725
/*
27422726
* The RTT bit can only be changed when translation is disabled,
@@ -2747,6 +2731,10 @@ static int copy_translation_tables(struct intel_iommu *iommu)
27472731
if (new_ext != ext)
27482732
return -EINVAL;
27492733

2734+
iommu->copied_tables = bitmap_zalloc(BIT_ULL(16), GFP_KERNEL);
2735+
if (!iommu->copied_tables)
2736+
return -ENOMEM;
2737+
27502738
old_rt_phys = rtaddr_reg & VTD_PAGE_MASK;
27512739
if (!old_rt_phys)
27522740
return -EINVAL;

drivers/iommu/intel/iommu.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,6 @@
197197
#define ecap_dis(e) (((e) >> 27) & 0x1)
198198
#define ecap_nest(e) (((e) >> 26) & 0x1)
199199
#define ecap_mts(e) (((e) >> 25) & 0x1)
200-
#define ecap_ecs(e) (((e) >> 24) & 0x1)
201200
#define ecap_iotlb_offset(e) ((((e) >> 8) & 0x3ff) * 16)
202201
#define ecap_max_iotlb_offset(e) (ecap_iotlb_offset(e) + 16)
203202
#define ecap_coherent(e) ((e) & 0x1)
@@ -265,7 +264,6 @@
265264
#define DMA_GSTS_CFIS (((u32)1) << 23)
266265

267266
/* DMA_RTADDR_REG */
268-
#define DMA_RTADDR_RTT (((u64)1) << 11)
269267
#define DMA_RTADDR_SMT (((u64)1) << 10)
270268

271269
/* CCMD_REG */
@@ -579,6 +577,7 @@ struct intel_iommu {
579577

580578
#ifdef CONFIG_INTEL_IOMMU
581579
unsigned long *domain_ids; /* bitmap of domains */
580+
unsigned long *copied_tables; /* bitmap of copied tables */
582581
spinlock_t lock; /* protect context, domain ids */
583582
struct root_entry *root_entry; /* virtual address */
584583

@@ -701,6 +700,11 @@ static inline int nr_pte_to_next_page(struct dma_pte *pte)
701700
(struct dma_pte *)ALIGN((unsigned long)pte, VTD_PAGE_SIZE) - pte;
702701
}
703702

703+
static inline bool context_present(struct context_entry *context)
704+
{
705+
return (context->lo & 1);
706+
}
707+
704708
extern struct dmar_drhd_unit * dmar_find_matched_drhd_unit(struct pci_dev *dev);
705709

706710
extern int dmar_enable_qi(struct intel_iommu *iommu);
@@ -784,7 +788,6 @@ static inline void intel_iommu_debugfs_init(void) {}
784788
#endif /* CONFIG_INTEL_IOMMU_DEBUGFS */
785789

786790
extern const struct attribute_group *intel_iommu_groups[];
787-
bool context_present(struct context_entry *context);
788791
struct context_entry *iommu_context_addr(struct intel_iommu *iommu, u8 bus,
789792
u8 devfn, int alloc);
790793

0 commit comments

Comments
 (0)