[PATCH] MIPS: Fix detection of unsupported highmem with cache aliases

From: Paul Burton
Date: Fri Sep 02 2016 - 10:18:10 EST


The paging_init() function contains code which detects that highmem is
in use but unsupported due to dcache aliasing. However this code was
ineffective because it was being run before the caches are probed,
meaning that cpu_has_dc_aliases would always evaluate to false (unless a
platform overrides it to a compile-time constant) and the detection of
the unsupported case is never triggered. The kernel would then go on to
attempt to use highmem & either hit coherency issues or trigger the
BUG_ON in flush_kernel_dcache_page().

Fix this by running paging_init() later than cpu_cache_init(), such that
the cpu_has_dc_aliases macro will evaluate correctly & the unsupported
highmem case will be detected successfully.

This then leads to a formerly hidden issue in that
mem_init_free_highmem() will attempt to free all highmem pages, even
though we're avoiding use of them & don't have valid page structs for
them. This leads to an invalid pointer dereference & a TLB exception.
Avoid this by skipping the loop in mem_init_free_highmem() if
cpu_has_dc_aliases evaluates true.

Signed-off-by: Paul Burton <paul.burton@xxxxxxxxxx>
---

arch/mips/kernel/setup.c | 2 +-
arch/mips/mm/init.c | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
index ef408a0..d840f02 100644
--- a/arch/mips/kernel/setup.c
+++ b/arch/mips/kernel/setup.c
@@ -757,7 +757,6 @@ static void __init arch_mem_init(char **cmdline_p)
device_tree_init();
sparse_init();
plat_swiotlb_setup();
- paging_init();

dma_contiguous_reserve(PFN_PHYS(max_low_pfn));
/* Tell bootmem about cma reserved memblock section */
@@ -870,6 +869,7 @@ void __init setup_arch(char **cmdline_p)
prefill_possible_map();

cpu_cache_init();
+ paging_init();
}

unsigned long kernelsp[NR_CPUS];
diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c
index 9b58eb5..ea6d8b6 100644
--- a/arch/mips/mm/init.c
+++ b/arch/mips/mm/init.c
@@ -441,6 +441,9 @@ static inline void mem_init_free_highmem(void)
#ifdef CONFIG_HIGHMEM
unsigned long tmp;

+ if (cpu_has_dc_aliases)
+ return;
+
for (tmp = highstart_pfn; tmp < highend_pfn; tmp++) {
struct page *page = pfn_to_page(tmp);

--
2.9.3