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

stacktrace.c (3362B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2008 ARM Limited
      4 * Copyright (C) 2014 Regents of the University of California
      5 */
      6
      7#include <linux/export.h>
      8#include <linux/kallsyms.h>
      9#include <linux/sched.h>
     10#include <linux/sched/debug.h>
     11#include <linux/sched/task_stack.h>
     12#include <linux/stacktrace.h>
     13#include <linux/ftrace.h>
     14
     15#include <asm/stacktrace.h>
     16
     17#ifdef CONFIG_FRAME_POINTER
     18
     19void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
     20			     bool (*fn)(void *, unsigned long), void *arg)
     21{
     22	unsigned long fp, sp, pc;
     23	int level = 0;
     24
     25	if (regs) {
     26		fp = frame_pointer(regs);
     27		sp = user_stack_pointer(regs);
     28		pc = instruction_pointer(regs);
     29	} else if (task == NULL || task == current) {
     30		fp = (unsigned long)__builtin_frame_address(0);
     31		sp = current_stack_pointer;
     32		pc = (unsigned long)walk_stackframe;
     33	} else {
     34		/* task blocked in __switch_to */
     35		fp = task->thread.s[0];
     36		sp = task->thread.sp;
     37		pc = task->thread.ra;
     38	}
     39
     40	for (;;) {
     41		unsigned long low, high;
     42		struct stackframe *frame;
     43
     44		if (unlikely(!__kernel_text_address(pc) || (level++ >= 1 && !fn(arg, pc))))
     45			break;
     46
     47		/* Validate frame pointer */
     48		low = sp + sizeof(struct stackframe);
     49		high = ALIGN(sp, THREAD_SIZE);
     50		if (unlikely(fp < low || fp > high || fp & 0x7))
     51			break;
     52		/* Unwind stack frame */
     53		frame = (struct stackframe *)fp - 1;
     54		sp = fp;
     55		if (regs && (regs->epc == pc) && (frame->fp & 0x7)) {
     56			fp = frame->ra;
     57			pc = regs->ra;
     58		} else {
     59			fp = frame->fp;
     60			pc = ftrace_graph_ret_addr(current, NULL, frame->ra,
     61						   (unsigned long *)(fp - 8));
     62		}
     63
     64	}
     65}
     66
     67#else /* !CONFIG_FRAME_POINTER */
     68
     69void notrace walk_stackframe(struct task_struct *task,
     70	struct pt_regs *regs, bool (*fn)(void *, unsigned long), void *arg)
     71{
     72	unsigned long sp, pc;
     73	unsigned long *ksp;
     74
     75	if (regs) {
     76		sp = user_stack_pointer(regs);
     77		pc = instruction_pointer(regs);
     78	} else if (task == NULL || task == current) {
     79		sp = current_stack_pointer;
     80		pc = (unsigned long)walk_stackframe;
     81	} else {
     82		/* task blocked in __switch_to */
     83		sp = task->thread.sp;
     84		pc = task->thread.ra;
     85	}
     86
     87	if (unlikely(sp & 0x7))
     88		return;
     89
     90	ksp = (unsigned long *)sp;
     91	while (!kstack_end(ksp)) {
     92		if (__kernel_text_address(pc) && unlikely(!fn(arg, pc)))
     93			break;
     94		pc = (*ksp++) - 0x4;
     95	}
     96}
     97
     98#endif /* CONFIG_FRAME_POINTER */
     99
    100static bool print_trace_address(void *arg, unsigned long pc)
    101{
    102	const char *loglvl = arg;
    103
    104	print_ip_sym(loglvl, pc);
    105	return true;
    106}
    107
    108noinline void dump_backtrace(struct pt_regs *regs, struct task_struct *task,
    109		    const char *loglvl)
    110{
    111	walk_stackframe(task, regs, print_trace_address, (void *)loglvl);
    112}
    113
    114void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl)
    115{
    116	pr_cont("%sCall Trace:\n", loglvl);
    117	dump_backtrace(NULL, task, loglvl);
    118}
    119
    120static bool save_wchan(void *arg, unsigned long pc)
    121{
    122	if (!in_sched_functions(pc)) {
    123		unsigned long *p = arg;
    124		*p = pc;
    125		return false;
    126	}
    127	return true;
    128}
    129
    130unsigned long __get_wchan(struct task_struct *task)
    131{
    132	unsigned long pc = 0;
    133
    134	if (!try_get_task_stack(task))
    135		return 0;
    136	walk_stackframe(task, NULL, save_wchan, &pc);
    137	put_task_stack(task);
    138	return pc;
    139}
    140
    141noinline void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,
    142		     struct task_struct *task, struct pt_regs *regs)
    143{
    144	walk_stackframe(task, regs, consume_entry, cookie);
    145}