forked from Minki/linux
d4664b6c98
Now that we no longer rely on the stack pointer to access the current task struct or thread info, we can implement support for IRQ stacks cleanly as well. Define a per-CPU IRQ stack and switch to this stack when taking an IRQ, provided that we were not already using that stack in the interrupted context. This is never the case for IRQs taken from user space, but ones taken while running in the kernel could fire while one taken from user space has not completed yet. Signed-off-by: Ard Biesheuvel <ardb@kernel.org> Acked-by: Linus Walleij <linus.walleij@linaro.org> Tested-by: Keith Packard <keithpac@amazon.com> Acked-by: Nick Desaulniers <ndesaulniers@google.com> Tested-by: Marc Zyngier <maz@kernel.org> Tested-by: Vladimir Murzin <vladimir.murzin@arm.com> # ARMv7M
132 lines
3.4 KiB
ArmAsm
132 lines
3.4 KiB
ArmAsm
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/*
|
|
* linux/arch/arm/lib/backtrace.S
|
|
*
|
|
* Copyright (C) 1995, 1996 Russell King
|
|
*
|
|
* 27/03/03 Ian Molton Clean up CONFIG_CPU
|
|
*/
|
|
#include <linux/kern_levels.h>
|
|
#include <linux/linkage.h>
|
|
#include <asm/assembler.h>
|
|
.text
|
|
|
|
@ fp is 0 or stack frame
|
|
|
|
#define frame r4
|
|
#define sv_fp r5
|
|
#define sv_pc r6
|
|
#define mask r7
|
|
#define offset r8
|
|
#define loglvl r9
|
|
|
|
ENTRY(c_backtrace)
|
|
|
|
#if !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK)
|
|
ret lr
|
|
ENDPROC(c_backtrace)
|
|
#else
|
|
stmfd sp!, {r4 - r9, lr} @ Save an extra register so we have a location...
|
|
movs frame, r0 @ if frame pointer is zero
|
|
beq no_frame @ we have no stack frames
|
|
mov loglvl, r2
|
|
|
|
tst r1, #0x10 @ 26 or 32-bit mode?
|
|
ARM( moveq mask, #0xfc000003 )
|
|
THUMB( moveq mask, #0xfc000000 )
|
|
THUMB( orreq mask, #0x03 )
|
|
movne mask, #0 @ mask for 32-bit
|
|
|
|
1: stmfd sp!, {pc} @ calculate offset of PC stored
|
|
ldr r0, [sp], #4 @ by stmfd for this CPU
|
|
adr r1, 1b
|
|
sub offset, r0, r1
|
|
|
|
/*
|
|
* Stack frame layout:
|
|
* optionally saved caller registers (r4 - r10)
|
|
* saved fp
|
|
* saved sp
|
|
* saved lr
|
|
* frame => saved pc
|
|
* optionally saved arguments (r0 - r3)
|
|
* saved sp => <next word>
|
|
*
|
|
* Functions start with the following code sequence:
|
|
* mov ip, sp
|
|
* stmfd sp!, {r0 - r3} (optional)
|
|
* corrected pc => stmfd sp!, {..., fp, ip, lr, pc}
|
|
*/
|
|
for_each_frame: tst frame, mask @ Check for address exceptions
|
|
bne no_frame
|
|
|
|
1001: ldr sv_pc, [frame, #0] @ get saved pc
|
|
1002: ldr sv_fp, [frame, #-12] @ get saved fp
|
|
|
|
sub sv_pc, sv_pc, offset @ Correct PC for prefetching
|
|
bic sv_pc, sv_pc, mask @ mask PC/LR for the mode
|
|
|
|
1003: ldr r2, [sv_pc, #-4] @ if stmfd sp!, {args} exists,
|
|
ldr r3, .Ldsi+4 @ adjust saved 'pc' back one
|
|
teq r3, r2, lsr #11 @ instruction
|
|
subne r0, sv_pc, #4 @ allow for mov
|
|
subeq r0, sv_pc, #8 @ allow for mov + stmia
|
|
|
|
ldr r1, [frame, #-4] @ get saved lr
|
|
mov r2, frame
|
|
bic r1, r1, mask @ mask PC/LR for the mode
|
|
mov r3, loglvl
|
|
bl dump_backtrace_entry
|
|
|
|
ldr r1, [sv_pc, #-4] @ if stmfd sp!, {args} exists,
|
|
ldr r3, .Ldsi+4
|
|
teq r3, r1, lsr #11
|
|
ldreq r0, [frame, #-8] @ get sp
|
|
subeq r0, r0, #4 @ point at the last arg
|
|
mov r2, loglvl
|
|
bleq dump_backtrace_stm @ dump saved registers
|
|
|
|
1004: ldr r1, [sv_pc, #0] @ if stmfd sp!, {..., fp, ip, lr, pc}
|
|
ldr r3, .Ldsi @ instruction exists,
|
|
teq r3, r1, lsr #11
|
|
subeq r0, frame, #16
|
|
mov r2, loglvl
|
|
bleq dump_backtrace_stm @ dump saved registers
|
|
|
|
teq sv_fp, #0 @ zero saved fp means
|
|
beq no_frame @ no further frames
|
|
|
|
cmp sv_fp, frame @ next frame must be
|
|
mov frame, sv_fp @ above the current frame
|
|
#ifdef CONFIG_IRQSTACKS
|
|
@
|
|
@ Kernel stacks may be discontiguous in memory. If the next
|
|
@ frame is below the previous frame, accept it as long as it
|
|
@ lives in kernel memory.
|
|
@
|
|
cmpls sv_fp, #PAGE_OFFSET
|
|
#endif
|
|
bhi for_each_frame
|
|
|
|
1006: adr r0, .Lbad
|
|
mov r1, loglvl
|
|
mov r2, frame
|
|
bl _printk
|
|
no_frame: ldmfd sp!, {r4 - r9, pc}
|
|
ENDPROC(c_backtrace)
|
|
|
|
.pushsection __ex_table,"a"
|
|
.align 3
|
|
.long 1001b, 1006b
|
|
.long 1002b, 1006b
|
|
.long 1003b, 1006b
|
|
.long 1004b, 1006b
|
|
.popsection
|
|
|
|
.Lbad: .asciz "%sBacktrace aborted due to bad frame pointer <%p>\n"
|
|
.align
|
|
.Ldsi: .word 0xe92dd800 >> 11 @ stmfd sp!, {... fp, ip, lr, pc}
|
|
.word 0xe92d0000 >> 11 @ stmfd sp!, {}
|
|
|
|
#endif
|