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 (2038B)


      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#include <asm/stacktrace.h>
      8
      9/*
     10 * Get the return address for a single stackframe and return a pointer to the
     11 * next frame tail.
     12 */
     13static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry,
     14				    unsigned long fp, unsigned long reg_ra)
     15{
     16	struct stackframe buftail;
     17	unsigned long ra = 0;
     18	unsigned long __user *user_frame_tail =
     19		(unsigned long __user *)(fp - sizeof(struct stackframe));
     20
     21	/* Check accessibility of one struct frame_tail beyond */
     22	if (!access_ok(user_frame_tail, sizeof(buftail)))
     23		return 0;
     24	if (__copy_from_user_inatomic(&buftail, user_frame_tail,
     25				      sizeof(buftail)))
     26		return 0;
     27
     28	if (reg_ra != 0)
     29		ra = reg_ra;
     30	else
     31		ra = buftail.ra;
     32
     33	fp = buftail.fp;
     34	if (ra != 0)
     35		perf_callchain_store(entry, ra);
     36	else
     37		return 0;
     38
     39	return fp;
     40}
     41
     42/*
     43 * This will be called when the target is in user mode
     44 * This function will only be called when we use
     45 * "PERF_SAMPLE_CALLCHAIN" in
     46 * kernel/events/core.c:perf_prepare_sample()
     47 *
     48 * How to trigger perf_callchain_[user/kernel] :
     49 * $ perf record -e cpu-clock --call-graph fp ./program
     50 * $ perf report --call-graph
     51 *
     52 * On RISC-V platform, the program being sampled and the C library
     53 * need to be compiled with -fno-omit-frame-pointer, otherwise
     54 * the user stack will not contain function frame.
     55 */
     56void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
     57			 struct pt_regs *regs)
     58{
     59	unsigned long fp = 0;
     60
     61	fp = regs->s0;
     62	perf_callchain_store(entry, regs->epc);
     63
     64	fp = user_backtrace(entry, fp, regs->ra);
     65	while (fp && !(fp & 0x3) && entry->nr < entry->max_stack)
     66		fp = user_backtrace(entry, fp, 0);
     67}
     68
     69static bool fill_callchain(void *entry, unsigned long pc)
     70{
     71	return perf_callchain_store(entry, pc) == 0;
     72}
     73
     74void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
     75			   struct pt_regs *regs)
     76{
     77	walk_stackframe(NULL, regs, fill_callchain, entry);
     78}