[PATCH] x86: Fix overlap of i386 CPU_ENTRY_AREA with FIX_BTMAP

From: William Grant
Date: Tue Jan 30 2018 - 06:23:07 EST


Since commit 92a0f81d8957 ("x86/cpu_entry_area: Move it out of the
fixmap"), i386's CPU_ENTRY_AREA has been mapped to the memory area just
below FIXADDR_START. But already immediately before FIXADDR_START is the
FIX_BTMAP area, which means that early_ioremap can collide with the
entry area.

It's especially bad on PAE where FIX_BTMAP_BEGIN gets aligned to exactly
match CPU_ENTRY_AREA_BASE, so the first early_ioremap slot clobbers the
IDT and causes interrupts during early boot to reset the system.

The overlap wasn't a problem before the CPU entry area was introduced,
as the fixmap has classically been preceded by the pkmap or vmalloc
areas, neither of which is used until early_ioremap is out of the
picture.

Relocate CPU_ENTRY_AREA to below FIX_BTMAP, not just below the permanent
fixmap area.

Signed-off-by: William Grant <william.grant@xxxxxxxxxxxxx>
---
This fixes a triple fault during early boot of 4.15 with PAE on x86 when
an Intel IOMMU is present. The ACPI table retrieval inside pci_iommu_alloc()
called early_ioremap and unmapped the IDT, and so the exception inside
test_wp_bit triple faulted.

2MiB of i386 kernel address space is wasted in the FIX_BTMAP area that
isn't used after boot, but I couldn't see a better place to put
CPU_ENTRY_AREA. We can't stick it just before the pkmap, since pkmap
doesn't exist when highmem is disabled. We could potentially shift fixmap
down from the top of the address space, but that's already variable due to
reserve_top_address. The only other option would be to shuffle it in
between lowmem and vmalloc, which looks messy due to their variable sizes.

arch/x86/include/asm/fixmap.h | 6 ++++--
arch/x86/include/asm/pgtable_32_types.h | 5 +++--
2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/arch/x86/include/asm/fixmap.h b/arch/x86/include/asm/fixmap.h
index 64c4a30e0d39..987020a28534 100644
--- a/arch/x86/include/asm/fixmap.h
+++ b/arch/x86/include/asm/fixmap.h
@@ -137,8 +137,10 @@ enum fixed_addresses {

extern void reserve_top_address(unsigned long reserve);

-#define FIXADDR_SIZE (__end_of_permanent_fixed_addresses << PAGE_SHIFT)
-#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE)
+#define FIXADDR_SIZE (__end_of_permanent_fixed_addresses << PAGE_SHIFT)
+#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE)
+#define FIXADDR_ALL_SIZE (__end_of_fixed_addresses << PAGE_SHIFT)
+#define FIXADDR_ALL_START (FIXADDR_TOP - FIXADDR_ALL_SIZE)

extern int fixmaps_set;

diff --git a/arch/x86/include/asm/pgtable_32_types.h b/arch/x86/include/asm/pgtable_32_types.h
index ce245b0cdfca..01973e9fd269 100644
--- a/arch/x86/include/asm/pgtable_32_types.h
+++ b/arch/x86/include/asm/pgtable_32_types.h
@@ -44,8 +44,9 @@ extern bool __vmalloc_start_set; /* set once high_memory is set */
*/
#define CPU_ENTRY_AREA_PAGES (NR_CPUS * 40)

-#define CPU_ENTRY_AREA_BASE \
- ((FIXADDR_START - PAGE_SIZE * (CPU_ENTRY_AREA_PAGES + 1)) & PMD_MASK)
+#define CPU_ENTRY_AREA_BASE \
+ ((FIXADDR_ALL_START - PAGE_SIZE * (CPU_ENTRY_AREA_PAGES + 1)) \
+ & PMD_MASK)

#define PKMAP_BASE \
((CPU_ENTRY_AREA_BASE - PAGE_SIZE) & PMD_MASK)
--
2.15.1