5e05153144
The current soft_restart() and setup_restart implementations incorrectly assume that compiler will not spill/fill values to/from stack. However this assumption seems to be wrong, revealed by the disassembly of the currently existing code (v3.16) built with Linaro GCC 4.9-2014.05. ffffffc000085224 <soft_restart>: ffffffc000085224: a9be7bfd stp x29, x30, [sp,#-32]! ffffffc000085228: 910003fd mov x29, sp ffffffc00008522c: f9000fa0 str x0, [x29,#24] ffffffc000085230: 94003d21 bl ffffffc0000946b4 <setup_mm_for_reboot> ffffffc000085234: 94003b33 bl ffffffc000093f00 <flush_cache_all> ffffffc000085238: 94003dfa bl ffffffc000094a20 <cpu_cache_off> ffffffc00008523c: 94003b31 bl ffffffc000093f00 <flush_cache_all> ffffffc000085240: b0003321 adrp x1, ffffffc0006ea000 <reset_devices> ffffffc000085244: f9400fa0 ldr x0, [x29,#24] ----> spilled addr ffffffc000085248: f942fc22 ldr x2, [x1,#1528] ----> global memstart_addr ffffffc00008524c: f0000061 adrp x1, ffffffc000094000 <__inval_cache_range+0x40> ffffffc000085250: 91290021 add x1, x1, #0xa40 ffffffc000085254: 8b010041 add x1, x2, x1 ffffffc000085258: d2c00802 mov x2, #0x4000000000 // #274877906944 ffffffc00008525c: 8b020021 add x1, x1, x2 ffffffc000085260: d63f0020 blr x1 ... Here the compiler generates memory accesses after the cache is disabled, loading stale values for the spilled value and global variable. As we cannot control when the compiler will access memory we must rewrite the functions in assembly to stash values we need in registers prior to disabling the cache, avoiding the use of memory. Reviewed-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Arun Chandran <achandran@mvista.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
55 lines
1.7 KiB
C
55 lines
1.7 KiB
C
/*
|
|
* Based on arch/arm/include/asm/proc-fns.h
|
|
*
|
|
* Copyright (C) 1997-1999 Russell King
|
|
* Copyright (C) 2000 Deep Blue Solutions Ltd
|
|
* Copyright (C) 2012 ARM Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifndef __ASM_PROCFNS_H
|
|
#define __ASM_PROCFNS_H
|
|
|
|
#ifdef __KERNEL__
|
|
#ifndef __ASSEMBLY__
|
|
|
|
#include <asm/page.h>
|
|
|
|
struct mm_struct;
|
|
struct cpu_suspend_ctx;
|
|
|
|
extern void cpu_cache_off(void);
|
|
extern void cpu_do_idle(void);
|
|
extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);
|
|
extern void cpu_reset(unsigned long addr) __attribute__((noreturn));
|
|
void cpu_soft_restart(phys_addr_t cpu_reset,
|
|
unsigned long addr) __attribute__((noreturn));
|
|
extern void cpu_do_suspend(struct cpu_suspend_ctx *ptr);
|
|
extern u64 cpu_do_resume(phys_addr_t ptr, u64 idmap_ttbr);
|
|
|
|
#include <asm/memory.h>
|
|
|
|
#define cpu_switch_mm(pgd,mm) cpu_do_switch_mm(virt_to_phys(pgd),mm)
|
|
|
|
#define cpu_get_pgd() \
|
|
({ \
|
|
unsigned long pg; \
|
|
asm("mrs %0, ttbr0_el1\n" \
|
|
: "=r" (pg)); \
|
|
pg &= ~0xffff000000003ffful; \
|
|
(pgd_t *)phys_to_virt(pg); \
|
|
})
|
|
|
|
#endif /* __ASSEMBLY__ */
|
|
#endif /* __KERNEL__ */
|
|
#endif /* __ASM_PROCFNS_H */
|