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

perf_callchain.c (2860B)


      1// SPDX-License-Identifier: GPL-2.0
      2// Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd.
      3
      4#include <linux/perf_event.h>
      5#include <linux/uaccess.h>
      6
      7/* Kernel callchain */
      8struct stackframe {
      9	unsigned long fp;
     10	unsigned long lr;
     11};
     12
     13static int unwind_frame_kernel(struct stackframe *frame)
     14{
     15	unsigned long low = (unsigned long)task_stack_page(current);
     16	unsigned long high = low + THREAD_SIZE;
     17
     18	if (unlikely(frame->fp < low || frame->fp > high))
     19		return -EPERM;
     20
     21	if (kstack_end((void *)frame->fp) || frame->fp & 0x3)
     22		return -EPERM;
     23
     24	*frame = *(struct stackframe *)frame->fp;
     25
     26	if (__kernel_text_address(frame->lr)) {
     27		int graph = 0;
     28
     29		frame->lr = ftrace_graph_ret_addr(NULL, &graph, frame->lr,
     30				NULL);
     31	}
     32	return 0;
     33}
     34
     35static void notrace walk_stackframe(struct stackframe *fr,
     36			struct perf_callchain_entry_ctx *entry)
     37{
     38	do {
     39		perf_callchain_store(entry, fr->lr);
     40	} while (unwind_frame_kernel(fr) >= 0);
     41}
     42
     43/*
     44 * Get the return address for a single stackframe and return a pointer to the
     45 * next frame tail.
     46 */
     47static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry,
     48			unsigned long fp, unsigned long reg_lr)
     49{
     50	struct stackframe buftail;
     51	unsigned long lr = 0;
     52	unsigned long __user *user_frame_tail = (unsigned long __user *)fp;
     53
     54	/* Check accessibility of one struct frame_tail beyond */
     55	if (!access_ok(user_frame_tail, sizeof(buftail)))
     56		return 0;
     57	if (__copy_from_user_inatomic(&buftail, user_frame_tail,
     58				      sizeof(buftail)))
     59		return 0;
     60
     61	if (reg_lr != 0)
     62		lr = reg_lr;
     63	else
     64		lr = buftail.lr;
     65
     66	fp = buftail.fp;
     67	perf_callchain_store(entry, lr);
     68
     69	return fp;
     70}
     71
     72/*
     73 * This will be called when the target is in user mode
     74 * This function will only be called when we use
     75 * "PERF_SAMPLE_CALLCHAIN" in
     76 * kernel/events/core.c:perf_prepare_sample()
     77 *
     78 * How to trigger perf_callchain_[user/kernel] :
     79 * $ perf record -e cpu-clock --call-graph fp ./program
     80 * $ perf report --call-graph
     81 *
     82 * On C-SKY platform, the program being sampled and the C library
     83 * need to be compiled with * -mbacktrace, otherwise the user
     84 * stack will not contain function frame.
     85 */
     86void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
     87			 struct pt_regs *regs)
     88{
     89	unsigned long fp = 0;
     90
     91	fp = regs->regs[4];
     92	perf_callchain_store(entry, regs->pc);
     93
     94	/*
     95	 * While backtrace from leaf function, lr is normally
     96	 * not saved inside frame on C-SKY, so get lr from pt_regs
     97	 * at the sample point. However, lr value can be incorrect if
     98	 * lr is used as temp register
     99	 */
    100	fp = user_backtrace(entry, fp, regs->lr);
    101
    102	while (fp && !(fp & 0x3) && entry->nr < entry->max_stack)
    103		fp = user_backtrace(entry, fp, 0);
    104}
    105
    106void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
    107			   struct pt_regs *regs)
    108{
    109	struct stackframe fr;
    110
    111	fr.fp = regs->regs[4];
    112	fr.lr = regs->lr;
    113	walk_stackframe(&fr, entry);
    114}