From: Sasha Levin Date: Tue, 10 Mar 2020 00:30:47 +0000 (-0400) Subject: fixes for 5.4 X-Git-Tag: v4.4.216~25 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7b8aeefcdb20df6f8af2cfbfda38ec859d1904c9;p=thirdparty%2Fkernel%2Fstable-queue.git fixes for 5.4 Signed-off-by: Sasha Levin --- diff --git a/queue-5.4/powerpc-convert-flush_icache_range-friends-to-c.patch b/queue-5.4/powerpc-convert-flush_icache_range-friends-to-c.patch new file mode 100644 index 00000000000..3ff2c21d2be --- /dev/null +++ b/queue-5.4/powerpc-convert-flush_icache_range-friends-to-c.patch @@ -0,0 +1,545 @@ +From bfbf8bd13508c31fb7591ab0483b4d657e0b084a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 4 Nov 2019 13:32:56 +1100 +Subject: powerpc: Convert flush_icache_range & friends to C + +From: Alastair D'Silva + +[ Upstream commit 23eb7f560a2a6a1b0dbaaaae8685da75314347e4 ] + +Similar to commit 22e9c88d486a +("powerpc/64: reuse PPC32 static inline flush_dcache_range()") +this patch converts the following ASM symbols to C: + flush_icache_range() + __flush_dcache_icache() + __flush_dcache_icache_phys() + +This was done as we discovered a long-standing bug where the length of the +range was truncated due to using a 32 bit shift instead of a 64 bit one. + +By converting these functions to C, it becomes easier to maintain. + +flush_dcache_icache_phys() retains a critical assembler section as we must +ensure there are no memory accesses while the data MMU is disabled +(authored by Christophe Leroy). Since this has no external callers, it has +also been made static, allowing the compiler to inline it within +flush_dcache_icache_page(). + +Signed-off-by: Alastair D'Silva +Signed-off-by: Christophe Leroy +Signed-off-by: Michael Ellerman +[mpe: Minor fixups, don't export __flush_dcache_icache()] +Link: https://lore.kernel.org/r/20191104023305.9581-5-alastair@au1.ibm.com +Signed-off-by: Sasha Levin +--- + arch/powerpc/include/asm/cache.h | 26 ++--- + arch/powerpc/include/asm/cacheflush.h | 24 ++--- + arch/powerpc/kernel/misc_32.S | 120 --------------------- + arch/powerpc/kernel/misc_64.S | 102 ------------------ + arch/powerpc/mm/mem.c | 150 +++++++++++++++++++++++++- + 5 files changed, 170 insertions(+), 252 deletions(-) + +diff --git a/arch/powerpc/include/asm/cache.h b/arch/powerpc/include/asm/cache.h +index afb88754e0e07..72b81015cebe9 100644 +--- a/arch/powerpc/include/asm/cache.h ++++ b/arch/powerpc/include/asm/cache.h +@@ -96,22 +96,7 @@ static inline u32 l1_icache_bytes(void) + } + + #endif +-#endif /* ! __ASSEMBLY__ */ +- +-#if defined(__ASSEMBLY__) +-/* +- * For a snooping icache, we still need a dummy icbi to purge all the +- * prefetched instructions from the ifetch buffers. We also need a sync +- * before the icbi to order the the actual stores to memory that might +- * have modified instructions with the icbi. +- */ +-#define PURGE_PREFETCHED_INS \ +- sync; \ +- icbi 0,r3; \ +- sync; \ +- isync + +-#else + #define __read_mostly __attribute__((__section__(".data..read_mostly"))) + + #ifdef CONFIG_PPC_BOOK3S_32 +@@ -145,6 +130,17 @@ static inline void dcbst(void *addr) + { + __asm__ __volatile__ ("dcbst 0, %0" : : "r"(addr) : "memory"); + } ++ ++static inline void icbi(void *addr) ++{ ++ asm volatile ("icbi 0, %0" : : "r"(addr) : "memory"); ++} ++ ++static inline void iccci(void *addr) ++{ ++ asm volatile ("iccci 0, %0" : : "r"(addr) : "memory"); ++} ++ + #endif /* !__ASSEMBLY__ */ + #endif /* __KERNEL__ */ + #endif /* _ASM_POWERPC_CACHE_H */ +diff --git a/arch/powerpc/include/asm/cacheflush.h b/arch/powerpc/include/asm/cacheflush.h +index ed57843ef4524..4a1c9f0200e1b 100644 +--- a/arch/powerpc/include/asm/cacheflush.h ++++ b/arch/powerpc/include/asm/cacheflush.h +@@ -42,24 +42,20 @@ extern void flush_dcache_page(struct page *page); + #define flush_dcache_mmap_lock(mapping) do { } while (0) + #define flush_dcache_mmap_unlock(mapping) do { } while (0) + +-extern void flush_icache_range(unsigned long, unsigned long); ++void flush_icache_range(unsigned long start, unsigned long stop); + extern void flush_icache_user_range(struct vm_area_struct *vma, + struct page *page, unsigned long addr, + int len); +-extern void __flush_dcache_icache(void *page_va); + extern void flush_dcache_icache_page(struct page *page); +-#if defined(CONFIG_PPC32) && !defined(CONFIG_BOOKE) +-extern void __flush_dcache_icache_phys(unsigned long physaddr); +-#else +-static inline void __flush_dcache_icache_phys(unsigned long physaddr) +-{ +- BUG(); +-} +-#endif +- +-/* +- * Write any modified data cache blocks out to memory and invalidate them. +- * Does not invalidate the corresponding instruction cache blocks. ++void __flush_dcache_icache(void *page); ++ ++/** ++ * flush_dcache_range(): Write any modified data cache blocks out to memory and ++ * invalidate them. Does not invalidate the corresponding instruction cache ++ * blocks. ++ * ++ * @start: the start address ++ * @stop: the stop address (exclusive) + */ + static inline void flush_dcache_range(unsigned long start, unsigned long stop) + { +diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S +index 82df4b09e79f4..f4e4a1926a7a5 100644 +--- a/arch/powerpc/kernel/misc_32.S ++++ b/arch/powerpc/kernel/misc_32.S +@@ -316,126 +316,6 @@ _GLOBAL(flush_instruction_cache) + EXPORT_SYMBOL(flush_instruction_cache) + #endif /* CONFIG_PPC_8xx */ + +-/* +- * Write any modified data cache blocks out to memory +- * and invalidate the corresponding instruction cache blocks. +- * This is a no-op on the 601. +- * +- * flush_icache_range(unsigned long start, unsigned long stop) +- */ +-_GLOBAL(flush_icache_range) +-#if defined(CONFIG_PPC_BOOK3S_601) || defined(CONFIG_E200) +- PURGE_PREFETCHED_INS +- blr /* for 601 and e200, do nothing */ +-#else +- rlwinm r3,r3,0,0,31 - L1_CACHE_SHIFT +- subf r4,r3,r4 +- addi r4,r4,L1_CACHE_BYTES - 1 +- srwi. r4,r4,L1_CACHE_SHIFT +- beqlr +- mtctr r4 +- mr r6,r3 +-1: dcbst 0,r3 +- addi r3,r3,L1_CACHE_BYTES +- bdnz 1b +- sync /* wait for dcbst's to get to ram */ +-#ifndef CONFIG_44x +- mtctr r4 +-2: icbi 0,r6 +- addi r6,r6,L1_CACHE_BYTES +- bdnz 2b +-#else +- /* Flash invalidate on 44x because we are passed kmapped addresses and +- this doesn't work for userspace pages due to the virtually tagged +- icache. Sigh. */ +- iccci 0, r0 +-#endif +- sync /* additional sync needed on g4 */ +- isync +- blr +-#endif +-_ASM_NOKPROBE_SYMBOL(flush_icache_range) +-EXPORT_SYMBOL(flush_icache_range) +- +-/* +- * Flush a particular page from the data cache to RAM. +- * Note: this is necessary because the instruction cache does *not* +- * snoop from the data cache. +- * This is a no-op on the 601 and e200 which have a unified cache. +- * +- * void __flush_dcache_icache(void *page) +- */ +-_GLOBAL(__flush_dcache_icache) +-#if defined(CONFIG_PPC_BOOK3S_601) || defined(CONFIG_E200) +- PURGE_PREFETCHED_INS +- blr +-#else +- rlwinm r3,r3,0,0,31-PAGE_SHIFT /* Get page base address */ +- li r4,PAGE_SIZE/L1_CACHE_BYTES /* Number of lines in a page */ +- mtctr r4 +- mr r6,r3 +-0: dcbst 0,r3 /* Write line to ram */ +- addi r3,r3,L1_CACHE_BYTES +- bdnz 0b +- sync +-#ifdef CONFIG_44x +- /* We don't flush the icache on 44x. Those have a virtual icache +- * and we don't have access to the virtual address here (it's +- * not the page vaddr but where it's mapped in user space). The +- * flushing of the icache on these is handled elsewhere, when +- * a change in the address space occurs, before returning to +- * user space +- */ +-BEGIN_MMU_FTR_SECTION +- blr +-END_MMU_FTR_SECTION_IFSET(MMU_FTR_TYPE_44x) +-#endif /* CONFIG_44x */ +- mtctr r4 +-1: icbi 0,r6 +- addi r6,r6,L1_CACHE_BYTES +- bdnz 1b +- sync +- isync +- blr +-#endif +- +-#ifndef CONFIG_BOOKE +-/* +- * Flush a particular page from the data cache to RAM, identified +- * by its physical address. We turn off the MMU so we can just use +- * the physical address (this may be a highmem page without a kernel +- * mapping). +- * +- * void __flush_dcache_icache_phys(unsigned long physaddr) +- */ +-_GLOBAL(__flush_dcache_icache_phys) +-#if defined(CONFIG_PPC_BOOK3S_601) || defined(CONFIG_E200) +- PURGE_PREFETCHED_INS +- blr /* for 601 and e200, do nothing */ +-#else +- mfmsr r10 +- rlwinm r0,r10,0,28,26 /* clear DR */ +- mtmsr r0 +- isync +- rlwinm r3,r3,0,0,31-PAGE_SHIFT /* Get page base address */ +- li r4,PAGE_SIZE/L1_CACHE_BYTES /* Number of lines in a page */ +- mtctr r4 +- mr r6,r3 +-0: dcbst 0,r3 /* Write line to ram */ +- addi r3,r3,L1_CACHE_BYTES +- bdnz 0b +- sync +- mtctr r4 +-1: icbi 0,r6 +- addi r6,r6,L1_CACHE_BYTES +- bdnz 1b +- sync +- mtmsr r10 /* restore DR */ +- isync +- blr +-#endif +-#endif /* CONFIG_BOOKE */ +- + /* + * Copy a whole page. We use the dcbz instruction on the destination + * to reduce memory traffic (it eliminates the unnecessary reads of +diff --git a/arch/powerpc/kernel/misc_64.S b/arch/powerpc/kernel/misc_64.S +index 9bc0aa9aeb654..ff20c253f2737 100644 +--- a/arch/powerpc/kernel/misc_64.S ++++ b/arch/powerpc/kernel/misc_64.S +@@ -49,108 +49,6 @@ _GLOBAL(call_do_irq) + mtlr r0 + blr + +- .section ".toc","aw" +-PPC64_CACHES: +- .tc ppc64_caches[TC],ppc64_caches +- .section ".text" +- +-/* +- * Write any modified data cache blocks out to memory +- * and invalidate the corresponding instruction cache blocks. +- * +- * flush_icache_range(unsigned long start, unsigned long stop) +- * +- * flush all bytes from start through stop-1 inclusive +- */ +- +-_GLOBAL_TOC(flush_icache_range) +-BEGIN_FTR_SECTION +- PURGE_PREFETCHED_INS +- blr +-END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE) +-/* +- * Flush the data cache to memory +- * +- * Different systems have different cache line sizes +- * and in some cases i-cache and d-cache line sizes differ from +- * each other. +- */ +- ld r10,PPC64_CACHES@toc(r2) +- lwz r7,DCACHEL1BLOCKSIZE(r10)/* Get cache block size */ +- addi r5,r7,-1 +- andc r6,r3,r5 /* round low to line bdy */ +- subf r8,r6,r4 /* compute length */ +- add r8,r8,r5 /* ensure we get enough */ +- lwz r9,DCACHEL1LOGBLOCKSIZE(r10) /* Get log-2 of cache block size */ +- srd. r8,r8,r9 /* compute line count */ +- beqlr /* nothing to do? */ +- mtctr r8 +-1: dcbst 0,r6 +- add r6,r6,r7 +- bdnz 1b +- sync +- +-/* Now invalidate the instruction cache */ +- +- lwz r7,ICACHEL1BLOCKSIZE(r10) /* Get Icache block size */ +- addi r5,r7,-1 +- andc r6,r3,r5 /* round low to line bdy */ +- subf r8,r6,r4 /* compute length */ +- add r8,r8,r5 +- lwz r9,ICACHEL1LOGBLOCKSIZE(r10) /* Get log-2 of Icache block size */ +- srd. r8,r8,r9 /* compute line count */ +- beqlr /* nothing to do? */ +- mtctr r8 +-2: icbi 0,r6 +- add r6,r6,r7 +- bdnz 2b +- isync +- blr +-_ASM_NOKPROBE_SYMBOL(flush_icache_range) +-EXPORT_SYMBOL(flush_icache_range) +- +-/* +- * Flush a particular page from the data cache to RAM. +- * Note: this is necessary because the instruction cache does *not* +- * snoop from the data cache. +- * +- * void __flush_dcache_icache(void *page) +- */ +-_GLOBAL(__flush_dcache_icache) +-/* +- * Flush the data cache to memory +- * +- * Different systems have different cache line sizes +- */ +- +-BEGIN_FTR_SECTION +- PURGE_PREFETCHED_INS +- blr +-END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE) +- +-/* Flush the dcache */ +- ld r7,PPC64_CACHES@toc(r2) +- clrrdi r3,r3,PAGE_SHIFT /* Page align */ +- lwz r4,DCACHEL1BLOCKSPERPAGE(r7) /* Get # dcache blocks per page */ +- lwz r5,DCACHEL1BLOCKSIZE(r7) /* Get dcache block size */ +- mr r6,r3 +- mtctr r4 +-0: dcbst 0,r6 +- add r6,r6,r5 +- bdnz 0b +- sync +- +-/* Now invalidate the icache */ +- +- lwz r4,ICACHEL1BLOCKSPERPAGE(r7) /* Get # icache blocks per page */ +- lwz r5,ICACHEL1BLOCKSIZE(r7) /* Get icache block size */ +- mtctr r4 +-1: icbi 0,r3 +- add r3,r3,r5 +- bdnz 1b +- isync +- blr +- + _GLOBAL(__bswapdi2) + EXPORT_SYMBOL(__bswapdi2) + srdi r8,r3,32 +diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c +index 9f5b32163bda3..6612796ea69c2 100644 +--- a/arch/powerpc/mm/mem.c ++++ b/arch/powerpc/mm/mem.c +@@ -348,6 +348,120 @@ void free_initmem(void) + free_initmem_default(POISON_FREE_INITMEM); + } + ++/** ++ * flush_coherent_icache() - if a CPU has a coherent icache, flush it ++ * @addr: The base address to use (can be any valid address, the whole cache will be flushed) ++ * Return true if the cache was flushed, false otherwise ++ */ ++static inline bool flush_coherent_icache(unsigned long addr) ++{ ++ /* ++ * For a snooping icache, we still need a dummy icbi to purge all the ++ * prefetched instructions from the ifetch buffers. We also need a sync ++ * before the icbi to order the the actual stores to memory that might ++ * have modified instructions with the icbi. ++ */ ++ if (cpu_has_feature(CPU_FTR_COHERENT_ICACHE)) { ++ mb(); /* sync */ ++ icbi((void *)addr); ++ mb(); /* sync */ ++ isync(); ++ return true; ++ } ++ ++ return false; ++} ++ ++/** ++ * invalidate_icache_range() - Flush the icache by issuing icbi across an address range ++ * @start: the start address ++ * @stop: the stop address (exclusive) ++ */ ++static void invalidate_icache_range(unsigned long start, unsigned long stop) ++{ ++ unsigned long shift = l1_icache_shift(); ++ unsigned long bytes = l1_icache_bytes(); ++ char *addr = (char *)(start & ~(bytes - 1)); ++ unsigned long size = stop - (unsigned long)addr + (bytes - 1); ++ unsigned long i; ++ ++ for (i = 0; i < size >> shift; i++, addr += bytes) ++ icbi(addr); ++ ++ mb(); /* sync */ ++ isync(); ++} ++ ++/** ++ * flush_icache_range: Write any modified data cache blocks out to memory ++ * and invalidate the corresponding blocks in the instruction cache ++ * ++ * Generic code will call this after writing memory, before executing from it. ++ * ++ * @start: the start address ++ * @stop: the stop address (exclusive) ++ */ ++void flush_icache_range(unsigned long start, unsigned long stop) ++{ ++ if (flush_coherent_icache(start)) ++ return; ++ ++ clean_dcache_range(start, stop); ++ ++ if (IS_ENABLED(CONFIG_44x)) { ++ /* ++ * Flash invalidate on 44x because we are passed kmapped ++ * addresses and this doesn't work for userspace pages due to ++ * the virtually tagged icache. ++ */ ++ iccci((void *)start); ++ mb(); /* sync */ ++ isync(); ++ } else ++ invalidate_icache_range(start, stop); ++} ++EXPORT_SYMBOL(flush_icache_range); ++ ++#if !defined(CONFIG_PPC_8xx) && !defined(CONFIG_PPC64) ++/** ++ * flush_dcache_icache_phys() - Flush a page by it's physical address ++ * @physaddr: the physical address of the page ++ */ ++static void flush_dcache_icache_phys(unsigned long physaddr) ++{ ++ unsigned long bytes = l1_dcache_bytes(); ++ unsigned long nb = PAGE_SIZE / bytes; ++ unsigned long addr = physaddr & PAGE_MASK; ++ unsigned long msr, msr0; ++ unsigned long loop1 = addr, loop2 = addr; ++ ++ msr0 = mfmsr(); ++ msr = msr0 & ~MSR_DR; ++ /* ++ * This must remain as ASM to prevent potential memory accesses ++ * while the data MMU is disabled ++ */ ++ asm volatile( ++ " mtctr %2;\n" ++ " mtmsr %3;\n" ++ " isync;\n" ++ "0: dcbst 0, %0;\n" ++ " addi %0, %0, %4;\n" ++ " bdnz 0b;\n" ++ " sync;\n" ++ " mtctr %2;\n" ++ "1: icbi 0, %1;\n" ++ " addi %1, %1, %4;\n" ++ " bdnz 1b;\n" ++ " sync;\n" ++ " mtmsr %5;\n" ++ " isync;\n" ++ : "+&r" (loop1), "+&r" (loop2) ++ : "r" (nb), "r" (msr), "i" (bytes), "r" (msr0) ++ : "ctr", "memory"); ++} ++#endif // !defined(CONFIG_PPC_8xx) && !defined(CONFIG_PPC64) ++ + /* + * This is called when a page has been modified by the kernel. + * It just marks the page as not i-cache clean. We do the i-cache +@@ -380,12 +494,46 @@ void flush_dcache_icache_page(struct page *page) + __flush_dcache_icache(start); + kunmap_atomic(start); + } else { +- __flush_dcache_icache_phys(page_to_pfn(page) << PAGE_SHIFT); ++ unsigned long addr = page_to_pfn(page) << PAGE_SHIFT; ++ ++ if (flush_coherent_icache(addr)) ++ return; ++ flush_dcache_icache_phys(addr); + } + #endif + } + EXPORT_SYMBOL(flush_dcache_icache_page); + ++/** ++ * __flush_dcache_icache(): Flush a particular page from the data cache to RAM. ++ * Note: this is necessary because the instruction cache does *not* ++ * snoop from the data cache. ++ * ++ * @page: the address of the page to flush ++ */ ++void __flush_dcache_icache(void *p) ++{ ++ unsigned long addr = (unsigned long)p; ++ ++ if (flush_coherent_icache(addr)) ++ return; ++ ++ clean_dcache_range(addr, addr + PAGE_SIZE); ++ ++ /* ++ * We don't flush the icache on 44x. Those have a virtual icache and we ++ * don't have access to the virtual address here (it's not the page ++ * vaddr but where it's mapped in user space). The flushing of the ++ * icache on these is handled elsewhere, when a change in the address ++ * space occurs, before returning to user space. ++ */ ++ ++ if (cpu_has_feature(MMU_FTR_TYPE_44x)) ++ return; ++ ++ invalidate_icache_range(addr, addr + PAGE_SIZE); ++} ++ + void clear_user_page(void *page, unsigned long vaddr, struct page *pg) + { + clear_page(page); +-- +2.20.1 + diff --git a/queue-5.4/powerpc-define-helpers-to-get-l1-icache-sizes.patch b/queue-5.4/powerpc-define-helpers-to-get-l1-icache-sizes.patch new file mode 100644 index 00000000000..06c76fd206d --- /dev/null +++ b/queue-5.4/powerpc-define-helpers-to-get-l1-icache-sizes.patch @@ -0,0 +1,116 @@ +From 14251a37e901a8d373d2adb7bbc2ed6e8107f074 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 4 Nov 2019 13:32:55 +1100 +Subject: powerpc: define helpers to get L1 icache sizes + +From: Alastair D'Silva + +[ Upstream commit 7a0745c5e03ff1129864bc6d80f5c4417e8d7893 ] + +This patch adds helpers to retrieve icache sizes, and renames the existing +helpers to make it clear that they are for dcache. + +Signed-off-by: Alastair D'Silva +Signed-off-by: Michael Ellerman +Link: https://lore.kernel.org/r/20191104023305.9581-4-alastair@au1.ibm.com +Signed-off-by: Sasha Levin +--- + arch/powerpc/include/asm/cache.h | 29 +++++++++++++++++++++++---- + arch/powerpc/include/asm/cacheflush.h | 12 +++++------ + 2 files changed, 31 insertions(+), 10 deletions(-) + +diff --git a/arch/powerpc/include/asm/cache.h b/arch/powerpc/include/asm/cache.h +index 45e3137ccd71c..afb88754e0e07 100644 +--- a/arch/powerpc/include/asm/cache.h ++++ b/arch/powerpc/include/asm/cache.h +@@ -55,25 +55,46 @@ struct ppc64_caches { + + extern struct ppc64_caches ppc64_caches; + +-static inline u32 l1_cache_shift(void) ++static inline u32 l1_dcache_shift(void) + { + return ppc64_caches.l1d.log_block_size; + } + +-static inline u32 l1_cache_bytes(void) ++static inline u32 l1_dcache_bytes(void) + { + return ppc64_caches.l1d.block_size; + } ++ ++static inline u32 l1_icache_shift(void) ++{ ++ return ppc64_caches.l1i.log_block_size; ++} ++ ++static inline u32 l1_icache_bytes(void) ++{ ++ return ppc64_caches.l1i.block_size; ++} + #else +-static inline u32 l1_cache_shift(void) ++static inline u32 l1_dcache_shift(void) + { + return L1_CACHE_SHIFT; + } + +-static inline u32 l1_cache_bytes(void) ++static inline u32 l1_dcache_bytes(void) + { + return L1_CACHE_BYTES; + } ++ ++static inline u32 l1_icache_shift(void) ++{ ++ return L1_CACHE_SHIFT; ++} ++ ++static inline u32 l1_icache_bytes(void) ++{ ++ return L1_CACHE_BYTES; ++} ++ + #endif + #endif /* ! __ASSEMBLY__ */ + +diff --git a/arch/powerpc/include/asm/cacheflush.h b/arch/powerpc/include/asm/cacheflush.h +index eef388f2659f4..ed57843ef4524 100644 +--- a/arch/powerpc/include/asm/cacheflush.h ++++ b/arch/powerpc/include/asm/cacheflush.h +@@ -63,8 +63,8 @@ static inline void __flush_dcache_icache_phys(unsigned long physaddr) + */ + static inline void flush_dcache_range(unsigned long start, unsigned long stop) + { +- unsigned long shift = l1_cache_shift(); +- unsigned long bytes = l1_cache_bytes(); ++ unsigned long shift = l1_dcache_shift(); ++ unsigned long bytes = l1_dcache_bytes(); + void *addr = (void *)(start & ~(bytes - 1)); + unsigned long size = stop - (unsigned long)addr + (bytes - 1); + unsigned long i; +@@ -89,8 +89,8 @@ static inline void flush_dcache_range(unsigned long start, unsigned long stop) + */ + static inline void clean_dcache_range(unsigned long start, unsigned long stop) + { +- unsigned long shift = l1_cache_shift(); +- unsigned long bytes = l1_cache_bytes(); ++ unsigned long shift = l1_dcache_shift(); ++ unsigned long bytes = l1_dcache_bytes(); + void *addr = (void *)(start & ~(bytes - 1)); + unsigned long size = stop - (unsigned long)addr + (bytes - 1); + unsigned long i; +@@ -108,8 +108,8 @@ static inline void clean_dcache_range(unsigned long start, unsigned long stop) + static inline void invalidate_dcache_range(unsigned long start, + unsigned long stop) + { +- unsigned long shift = l1_cache_shift(); +- unsigned long bytes = l1_cache_bytes(); ++ unsigned long shift = l1_dcache_shift(); ++ unsigned long bytes = l1_dcache_bytes(); + void *addr = (void *)(start & ~(bytes - 1)); + unsigned long size = stop - (unsigned long)addr + (bytes - 1); + unsigned long i; +-- +2.20.1 + diff --git a/queue-5.4/powerpc-mm-fix-missing-kuap-disable-in-flush_coheren.patch b/queue-5.4/powerpc-mm-fix-missing-kuap-disable-in-flush_coheren.patch new file mode 100644 index 00000000000..6f7bd0160e2 --- /dev/null +++ b/queue-5.4/powerpc-mm-fix-missing-kuap-disable-in-flush_coheren.patch @@ -0,0 +1,92 @@ +From 87892beab5dde8ba26f7b211eeba745481ba7eb4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 3 Mar 2020 23:28:47 +1100 +Subject: powerpc/mm: Fix missing KUAP disable in flush_coherent_icache() + +From: Michael Ellerman + +[ Upstream commit 59bee45b9712c759ea4d3dcc4eff1752f3a66558 ] + +Stefan reported a strange kernel fault which turned out to be due to a +missing KUAP disable in flush_coherent_icache() called from +flush_icache_range(). + +The fault looks like: + + Kernel attempted to access user page (7fffc30d9c00) - exploit attempt? (uid: 1009) + BUG: Unable to handle kernel data access on read at 0x7fffc30d9c00 + Faulting instruction address: 0xc00000000007232c + Oops: Kernel access of bad area, sig: 11 [#1] + LE PAGE_SIZE=64K MMU=Radix SMP NR_CPUS=2048 NUMA PowerNV + CPU: 35 PID: 5886 Comm: sigtramp Not tainted 5.6.0-rc2-gcc-8.2.0-00003-gfc37a1632d40 #79 + NIP: c00000000007232c LR: c00000000003b7fc CTR: 0000000000000000 + REGS: c000001e11093940 TRAP: 0300 Not tainted (5.6.0-rc2-gcc-8.2.0-00003-gfc37a1632d40) + MSR: 900000000280b033 CR: 28000884 XER: 00000000 + CFAR: c0000000000722fc DAR: 00007fffc30d9c00 DSISR: 08000000 IRQMASK: 0 + GPR00: c00000000003b7fc c000001e11093bd0 c0000000023ac200 00007fffc30d9c00 + GPR04: 00007fffc30d9c18 0000000000000000 c000001e11093bd4 0000000000000000 + GPR08: 0000000000000000 0000000000000001 0000000000000000 c000001e1104ed80 + GPR12: 0000000000000000 c000001fff6ab380 c0000000016be2d0 4000000000000000 + GPR16: c000000000000000 bfffffffffffffff 0000000000000000 0000000000000000 + GPR20: 00007fffc30d9c00 00007fffc30d8f58 00007fffc30d9c18 00007fffc30d9c20 + GPR24: 00007fffc30d9c18 0000000000000000 c000001e11093d90 c000001e1104ed80 + GPR28: c000001e11093e90 0000000000000000 c0000000023d9d18 00007fffc30d9c00 + NIP flush_icache_range+0x5c/0x80 + LR handle_rt_signal64+0x95c/0xc2c + Call Trace: + 0xc000001e11093d90 (unreliable) + handle_rt_signal64+0x93c/0xc2c + do_notify_resume+0x310/0x430 + ret_from_except_lite+0x70/0x74 + Instruction dump: + 409e002c 7c0802a6 3c62ff31 3863f6a0 f8010080 48195fed 60000000 48fe4c8d + 60000000 e8010080 7c0803a6 7c0004ac <7c00ffac> 7c0004ac 4c00012c 38210070 + +This path through handle_rt_signal64() to setup_trampoline() and +flush_icache_range() is only triggered by 64-bit processes that have +unmapped their VDSO, which is rare. + +flush_icache_range() takes a range of addresses to flush. In +flush_coherent_icache() we implement an optimisation for CPUs where we +know we don't actually have to flush the whole range, we just need to +do a single icbi. + +However we still execute the icbi on the user address of the start of +the range we're flushing. On CPUs that also implement KUAP (Power9) +that leads to the spurious fault above. + +We should be able to pass any address, including a kernel address, to +the icbi on these CPUs, which would avoid any interaction with KUAP. +But I don't want to make that change in a bug fix, just in case it +surfaces some strange behaviour on some CPU. + +So for now just disable KUAP around the icbi. Note the icbi is treated +as a load, so we allow read access, not write as you'd expect. + +Fixes: 890274c2dc4c ("powerpc/64s: Implement KUAP for Radix MMU") +Cc: stable@vger.kernel.org # v5.2+ +Reported-by: Stefan Berger +Signed-off-by: Michael Ellerman +Link: https://lore.kernel.org/r/20200303235708.26004-1-mpe@ellerman.id.au +Signed-off-by: Sasha Levin +--- + arch/powerpc/mm/mem.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c +index 6612796ea69c2..96ca90ce0264a 100644 +--- a/arch/powerpc/mm/mem.c ++++ b/arch/powerpc/mm/mem.c +@@ -363,7 +363,9 @@ static inline bool flush_coherent_icache(unsigned long addr) + */ + if (cpu_has_feature(CPU_FTR_COHERENT_ICACHE)) { + mb(); /* sync */ ++ allow_read_from_user((const void __user *)addr, L1_CACHE_BYTES); + icbi((void *)addr); ++ prevent_read_from_user((const void __user *)addr, L1_CACHE_BYTES); + mb(); /* sync */ + isync(); + return true; +-- +2.20.1 + diff --git a/queue-5.4/series b/queue-5.4/series index 34e12817aa2..f3ca991eca1 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -120,3 +120,6 @@ asoc-topology-fix-memleak-in-soc_tplg_manifest_load.patch asoc-sof-fix-snd_sof_ipc_stream_posn.patch asoc-intel-skl-fix-pin-debug-prints.patch asoc-intel-skl-fix-possible-buffer-overflow-in-debug-outputs.patch +powerpc-define-helpers-to-get-l1-icache-sizes.patch +powerpc-convert-flush_icache_range-friends-to-c.patch +powerpc-mm-fix-missing-kuap-disable-in-flush_coheren.patch