cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

backtrace-clang.S (7226B)


      1/* SPDX-License-Identifier: GPL-2.0-only */
      2/*
      3 *  linux/arch/arm/lib/backtrace-clang.S
      4 *
      5 *  Copyright (C) 2019 Nathan Huckleberry
      6 *
      7 */
      8#include <linux/kern_levels.h>
      9#include <linux/linkage.h>
     10#include <asm/assembler.h>
     11		.text
     12
     13/* fp is 0 or stack frame */
     14
     15#define frame	r4
     16#define sv_fp	r5
     17#define sv_pc	r6
     18#define mask	r7
     19#define sv_lr	r8
     20#define loglvl	r9
     21
     22ENTRY(c_backtrace)
     23
     24#if !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK)
     25		ret	lr
     26ENDPROC(c_backtrace)
     27#else
     28
     29
     30/*
     31 * Clang does not store pc or sp in function prologues so we don't know exactly
     32 * where the function starts.
     33 *
     34 * We can treat the current frame's lr as the saved pc and the preceding
     35 * frame's lr as the current frame's lr, but we can't trace the most recent
     36 * call.  Inserting a false stack frame allows us to reference the function
     37 * called last in the stacktrace.
     38 *
     39 * If the call instruction was a bl we can look at the callers branch
     40 * instruction to calculate the saved pc.  We can recover the pc in most cases,
     41 * but in cases such as calling function pointers we cannot. In this case,
     42 * default to using the lr. This will be some address in the function, but will
     43 * not be the function start.
     44 *
     45 * Unfortunately due to the stack frame layout we can't dump r0 - r3, but these
     46 * are less frequently saved.
     47 *
     48 * Stack frame layout:
     49 * 		<larger addresses>
     50 * 		saved lr
     51 * 	frame=> saved fp
     52 * 		optionally saved caller registers (r4 - r10)
     53 * 		optionally saved arguments (r0 - r3)
     54 * 		<top of stack frame>
     55 * 		<smaller addresses>
     56 *
     57 * Functions start with the following code sequence:
     58 * corrected pc =>  stmfd sp!, {..., fp, lr}
     59 *		add fp, sp, #x
     60 *		stmfd sp!, {r0 - r3} (optional)
     61 *
     62 *
     63 *
     64 *
     65 *
     66 *
     67 * The diagram below shows an example stack setup for dump_stack.
     68 *
     69 * The frame for c_backtrace has pointers to the code of dump_stack. This is
     70 * why the frame of c_backtrace is used to for the pc calculation of
     71 * dump_stack. This is why we must move back a frame to print dump_stack.
     72 *
     73 * The stored locals for dump_stack are in dump_stack's frame. This means that
     74 * to fully print dump_stack's frame we need both the frame for dump_stack (for
     75 * locals) and the frame that was called by dump_stack (for pc).
     76 *
     77 * To print locals we must know where the function start is. If we read the
     78 * function prologue opcodes we can determine which variables are stored in the
     79 * stack frame.
     80 *
     81 * To find the function start of dump_stack we can look at the stored LR of
     82 * show_stack. It points at the instruction directly after the bl dump_stack.
     83 * We can then read the offset from the bl opcode to determine where the branch
     84 * takes us.  The address calculated must be the start of dump_stack.
     85 *
     86 * c_backtrace frame           dump_stack:
     87 * {[LR]    }  ============|   ...
     88 * {[FP]    }  =======|    |   bl c_backtrace
     89 *                    |    |=> ...
     90 * {[R4-R10]}         |
     91 * {[R0-R3] }         |        show_stack:
     92 * dump_stack frame   |        ...
     93 * {[LR]    } =============|   bl dump_stack
     94 * {[FP]    } <=======|    |=> ...
     95 * {[R4-R10]}
     96 * {[R0-R3] }
     97 */
     98
     99		stmfd	sp!, {r4 - r9, fp, lr}	@ Save an extra register
    100						@ to ensure 8 byte alignment
    101		movs	frame, r0		@ if frame pointer is zero
    102		beq	no_frame		@ we have no stack frames
    103		mov	loglvl, r2
    104		tst	r1, #0x10		@ 26 or 32-bit mode?
    105		moveq	mask, #0xfc000003
    106		movne	mask, #0		@ mask for 32-bit
    107
    108/*
    109 * Switches the current frame to be the frame for dump_stack.
    110 */
    111		add	frame, sp, #24		@ switch to false frame
    112for_each_frame:	tst	frame, mask		@ Check for address exceptions
    113		bne	no_frame
    114
    115/*
    116 * sv_fp is the stack frame with the locals for the current considered
    117 * function.
    118 *
    119 * sv_pc is the saved lr frame the frame above. This is a pointer to a code
    120 * address within the current considered function, but it is not the function
    121 * start. This value gets updated to be the function start later if it is
    122 * possible.
    123 */
    1241001:		ldr	sv_pc, [frame, #4]	@ get saved 'pc'
    1251002:		ldr	sv_fp, [frame, #0]	@ get saved fp
    126
    127		teq	sv_fp, mask		@ make sure next frame exists
    128		beq	no_frame
    129
    130/*
    131 * sv_lr is the lr from the function that called the current function. This is
    132 * a pointer to a code address in the current function's caller.  sv_lr-4 is
    133 * the instruction used to call the current function.
    134 *
    135 * This sv_lr can be used to calculate the function start if the function was
    136 * called using a bl instruction. If the function start can be recovered sv_pc
    137 * is overwritten with the function start.
    138 *
    139 * If the current function was called using a function pointer we cannot
    140 * recover the function start and instead continue with sv_pc as an arbitrary
    141 * value within the current function. If this is the case we cannot print
    142 * registers for the current function, but the stacktrace is still printed
    143 * properly.
    144 */
    1451003:		ldr	sv_lr, [sv_fp, #4]	@ get saved lr from next frame
    146
    1471004:		ldr	r0, [sv_lr, #-4]	@ get call instruction
    148		ldr	r3, .Lopcode+4
    149		and	r2, r3, r0		@ is this a bl call
    150		teq	r2, r3
    151		bne	finished_setup		@ give up if it's not
    152		and	r0, #0xffffff		@ get call offset 24-bit int
    153		lsl	r0, r0, #8		@ sign extend offset
    154		asr	r0, r0, #8
    155		ldr	sv_pc, [sv_fp, #4]	@ get lr address
    156		add	sv_pc, sv_pc, #-4	@ get call instruction address
    157		add	sv_pc, sv_pc, #8	@ take care of prefetch
    158		add	sv_pc, sv_pc, r0, lsl #2@ find function start
    159
    160finished_setup:
    161
    162		bic	sv_pc, sv_pc, mask	@ mask PC/LR for the mode
    163
    164/*
    165 * Print the function (sv_pc) and where it was called from (sv_lr).
    166 */
    167		mov	r0, sv_pc
    168
    169		mov	r1, sv_lr
    170		mov	r2, frame
    171		bic	r1, r1, mask		@ mask PC/LR for the mode
    172		mov	r3, loglvl
    173		bl	dump_backtrace_entry
    174
    175/*
    176 * Test if the function start is a stmfd instruction to determine which
    177 * registers were stored in the function prologue.
    178 *
    179 * If we could not recover the sv_pc because we were called through a function
    180 * pointer the comparison will fail and no registers will print. Unwinding will
    181 * continue as if there had been no registers stored in this frame.
    182 */
    1831005:		ldr	r1, [sv_pc, #0]		@ if stmfd sp!, {..., fp, lr}
    184		ldr	r3, .Lopcode		@ instruction exists,
    185		teq	r3, r1, lsr #11
    186		ldr	r0, [frame]		@ locals are stored in
    187						@ the preceding frame
    188		subeq	r0, r0, #4
    189		mov	r2, loglvl
    190		bleq	dump_backtrace_stm	@ dump saved registers
    191
    192/*
    193 * If we are out of frames or if the next frame is invalid.
    194 */
    195		teq	sv_fp, #0		@ zero saved fp means
    196		beq	no_frame		@ no further frames
    197
    198		cmp	sv_fp, frame		@ next frame must be
    199		mov	frame, sv_fp		@ above the current frame
    200#ifdef CONFIG_IRQSTACKS
    201		@
    202		@ Kernel stacks may be discontiguous in memory. If the next
    203		@ frame is below the previous frame, accept it as long as it
    204		@ lives in kernel memory.
    205		@
    206		cmpls	sv_fp, #PAGE_OFFSET
    207#endif
    208		bhi	for_each_frame
    209
    2101006:		adr	r0, .Lbad
    211		mov	r1, loglvl
    212		mov	r2, frame
    213		bl	_printk
    214no_frame:	ldmfd	sp!, {r4 - r9, fp, pc}
    215ENDPROC(c_backtrace)
    216		.pushsection __ex_table,"a"
    217		.align	3
    218		.long	1001b, 1006b
    219		.long	1002b, 1006b
    220		.long	1003b, 1006b
    221		.long	1004b, finished_setup
    222		.long   1005b, 1006b
    223		.popsection
    224
    225.Lbad:		.asciz	"%sBacktrace aborted due to bad frame pointer <%p>\n"
    226		.align
    227.Lopcode:	.word	0xe92d4800 >> 11	@ stmfd sp!, {... fp, lr}
    228		.word	0x0b000000		@ bl if these bits are set
    229
    230#endif