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

callchain.c (2749B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Performance counter callchain support - powerpc architecture code
      4 *
      5 * Copyright © 2009 Paul Mackerras, IBM Corporation.
      6 */
      7#include <linux/kernel.h>
      8#include <linux/sched.h>
      9#include <linux/perf_event.h>
     10#include <linux/percpu.h>
     11#include <linux/uaccess.h>
     12#include <linux/mm.h>
     13#include <asm/ptrace.h>
     14#include <asm/sigcontext.h>
     15#include <asm/ucontext.h>
     16#include <asm/vdso.h>
     17#include <asm/pte-walk.h>
     18
     19#include "callchain.h"
     20
     21/*
     22 * Is sp valid as the address of the next kernel stack frame after prev_sp?
     23 * The next frame may be in a different stack area but should not go
     24 * back down in the same stack area.
     25 */
     26static int valid_next_sp(unsigned long sp, unsigned long prev_sp)
     27{
     28	if (sp & 0xf)
     29		return 0;		/* must be 16-byte aligned */
     30	if (!validate_sp(sp, current, STACK_FRAME_OVERHEAD))
     31		return 0;
     32	if (sp >= prev_sp + STACK_FRAME_MIN_SIZE)
     33		return 1;
     34	/*
     35	 * sp could decrease when we jump off an interrupt stack
     36	 * back to the regular process stack.
     37	 */
     38	if ((sp & ~(THREAD_SIZE - 1)) != (prev_sp & ~(THREAD_SIZE - 1)))
     39		return 1;
     40	return 0;
     41}
     42
     43void __no_sanitize_address
     44perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs)
     45{
     46	unsigned long sp, next_sp;
     47	unsigned long next_ip;
     48	unsigned long lr;
     49	long level = 0;
     50	unsigned long *fp;
     51
     52	lr = regs->link;
     53	sp = regs->gpr[1];
     54	perf_callchain_store(entry, perf_instruction_pointer(regs));
     55
     56	if (!validate_sp(sp, current, STACK_FRAME_OVERHEAD))
     57		return;
     58
     59	for (;;) {
     60		fp = (unsigned long *) sp;
     61		next_sp = fp[0];
     62
     63		if (next_sp == sp + STACK_INT_FRAME_SIZE &&
     64		    fp[STACK_FRAME_MARKER] == STACK_FRAME_REGS_MARKER) {
     65			/*
     66			 * This looks like an interrupt frame for an
     67			 * interrupt that occurred in the kernel
     68			 */
     69			regs = (struct pt_regs *)(sp + STACK_FRAME_OVERHEAD);
     70			next_ip = regs->nip;
     71			lr = regs->link;
     72			level = 0;
     73			perf_callchain_store_context(entry, PERF_CONTEXT_KERNEL);
     74
     75		} else {
     76			if (level == 0)
     77				next_ip = lr;
     78			else
     79				next_ip = fp[STACK_FRAME_LR_SAVE];
     80
     81			/*
     82			 * We can't tell which of the first two addresses
     83			 * we get are valid, but we can filter out the
     84			 * obviously bogus ones here.  We replace them
     85			 * with 0 rather than removing them entirely so
     86			 * that userspace can tell which is which.
     87			 */
     88			if ((level == 1 && next_ip == lr) ||
     89			    (level <= 1 && !kernel_text_address(next_ip)))
     90				next_ip = 0;
     91
     92			++level;
     93		}
     94
     95		perf_callchain_store(entry, next_ip);
     96		if (!valid_next_sp(next_sp, sp))
     97			return;
     98		sp = next_sp;
     99	}
    100}
    101
    102void
    103perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs)
    104{
    105	if (!is_32bit_task())
    106		perf_callchain_user_64(entry, regs);
    107	else
    108		perf_callchain_user_32(entry, regs);
    109}