Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f5074429 authored by Mike Frysinger's avatar Mike Frysinger
Browse files

Blackfin: add support for dynamic ftrace

parent 67df6cc6
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ config BLACKFIN
	def_bool y
	select HAVE_ARCH_KGDB
	select HAVE_ARCH_TRACEHOOK
	select HAVE_DYNAMIC_FTRACE
	select HAVE_FTRACE_MCOUNT_RECORD
	select HAVE_FUNCTION_GRAPH_TRACER
	select HAVE_FUNCTION_TRACER
	select HAVE_FUNCTION_TRACE_MCOUNT_TEST
+16 −0
Original line number Diff line number Diff line
@@ -12,6 +12,22 @@

#ifndef __ASSEMBLY__

#ifdef CONFIG_DYNAMIC_FTRACE

extern void _mcount(void);
#define MCOUNT_ADDR ((unsigned long)_mcount)

static inline unsigned long ftrace_call_adjust(unsigned long addr)
{
	return addr;
}

struct dyn_arch_ftrace {
	/* No extra data needed for Blackfin */
};

#endif

#ifdef CONFIG_FRAME_POINTER
#include <linux/mm.h>

+1 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ else
    obj-y += time.o
endif

obj-$(CONFIG_DYNAMIC_FTRACE)         += ftrace.o
obj-$(CONFIG_FUNCTION_TRACER)        += ftrace-entry.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER)  += ftrace.o
CFLAGS_REMOVE_ftrace.o = -pg
+77 −8
Original line number Diff line number Diff line
@@ -10,6 +10,18 @@

.text

#ifdef CONFIG_DYNAMIC_FTRACE

/* Simple stub so we can boot the kernel until runtime patching has
 * disabled all calls to this.  Then it'll be unused.
 */
ENTRY(__mcount)
# if ANOMALY_05000371
	nop; nop; nop; nop;
# endif
	rts;
ENDPROC(__mcount)

/* GCC will have called us before setting up the function prologue, so we
 * can clobber the normal scratch registers, but we need to make sure to
 * save/restore the registers used for argument passing (R0-R2) in case
@@ -20,6 +32,56 @@
 * function.  And since GCC pushed the previous RETS for us, the previous
 * function will be waiting there.  mmmm pie.
 */
ENTRY(_ftrace_caller)
# ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
	/* optional micro optimization: return if stopped */
	p1.l = _function_trace_stop;
	p1.h = _function_trace_stop;
	r3 = [p1];
	cc = r3 == 0;
	if ! cc jump _ftrace_stub (bp);
# endif

	/* save first/second/third function arg and the return register */
	[--sp] = r2;
	[--sp] = r0;
	[--sp] = r1;
	[--sp] = rets;

	/* function_trace_call(unsigned long ip, unsigned long parent_ip):
	 *  ip: this point was called by ...
	 *  parent_ip: ... this function
	 * the ip itself will need adjusting for the mcount call
	 */
	r0 = rets;
	r1 = [sp + 16];	/* skip the 4 local regs on stack */
	r0 += -MCOUNT_INSN_SIZE;

.globl _ftrace_call
_ftrace_call:
	call _ftrace_stub

# ifdef CONFIG_FUNCTION_GRAPH_TRACER
.globl _ftrace_graph_call
_ftrace_graph_call:
	nop;	/* jump _ftrace_graph_caller; */
# endif

	/* restore state and get out of dodge */
.Lfinish_trace:
	rets = [sp++];
	r1 = [sp++];
	r0 = [sp++];
	r2 = [sp++];

.globl _ftrace_stub
_ftrace_stub:
	rts;
ENDPROC(_ftrace_caller)

#else

/* See documentation for _ftrace_caller */
ENTRY(__mcount)
# ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
	/* optional micro optimization: return if stopped */
@@ -103,6 +165,8 @@ _ftrace_stub:
	rts;
ENDPROC(__mcount)

#endif

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
/* The prepare_ftrace_return() function is similar to the trace function
 * except it takes a pointer to the location of the frompc.  This is so
@@ -110,6 +174,7 @@ ENDPROC(__mcount)
 * purposes.
 */
ENTRY(_ftrace_graph_caller)
# ifndef CONFIG_DYNAMIC_FTRACE
	/* save first/second function arg and the return register */
	[--sp] = r0;
	[--sp] = r1;
@@ -118,6 +183,10 @@ ENTRY(_ftrace_graph_caller)
	/* prepare_ftrace_return(parent, self_addr, frame_pointer) */
	r0 = sp;	/* unsigned long *parent */
	r1 = rets;	/* unsigned long self_addr */
# else
	r0 = sp;	/* unsigned long *parent */
	r1 = [sp];	/* unsigned long self_addr */
# endif
# ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST
	r2 = fp;	/* unsigned long frame_pointer */
# endif
+85 −1
Original line number Diff line number Diff line
/*
 * ftrace graph code
 *
 * Copyright (C) 2009 Analog Devices Inc.
 * Copyright (C) 2009-2010 Analog Devices Inc.
 * Licensed under the GPL-2 or later.
 */

#include <linux/ftrace.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <asm/atomic.h>
#include <asm/cacheflush.h>

#ifdef CONFIG_DYNAMIC_FTRACE

static const unsigned char mnop[] = {
	0x03, 0xc0, 0x00, 0x18, /* MNOP; */
	0x03, 0xc0, 0x00, 0x18, /* MNOP; */
};

static void bfin_make_pcrel24(unsigned char *insn, unsigned long src,
                              unsigned long dst)
{
	uint32_t pcrel = (dst - src) >> 1;
	insn[0] = pcrel >> 16;
	insn[1] = 0xe3;
	insn[2] = pcrel;
	insn[3] = pcrel >> 8;
}
#define bfin_make_pcrel24(insn, src, dst) bfin_make_pcrel24(insn, src, (unsigned long)(dst))

static int ftrace_modify_code(unsigned long ip, const unsigned char *code,
                              unsigned long len)
{
	int ret = probe_kernel_write((void *)ip, (void *)code, len);
	flush_icache_range(ip, ip + len);
	return ret;
}

int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
                    unsigned long addr)
{
	/* Turn the mcount call site into two MNOPs as those are 32bit insns */
	return ftrace_modify_code(rec->ip, mnop, sizeof(mnop));
}

int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
{
	/* Restore the mcount call site */
	unsigned char call[8];
	call[0] = 0x67; /* [--SP] = RETS; */
	call[1] = 0x01;
	bfin_make_pcrel24(&call[2], rec->ip + 2, addr);
	call[6] = 0x27; /* RETS = [SP++]; */
	call[7] = 0x01;
	return ftrace_modify_code(rec->ip, call, sizeof(call));
}

int ftrace_update_ftrace_func(ftrace_func_t func)
{
	unsigned char call[4];
	unsigned long ip = (unsigned long)&ftrace_call;
	bfin_make_pcrel24(call, ip, func);
	return ftrace_modify_code(ip, call, sizeof(call));
}

int __init ftrace_dyn_arch_init(void *data)
{
	/* return value is done indirectly via data */
	*(unsigned long *)data = 0;

	return 0;
}

#endif

#ifdef CONFIG_FUNCTION_GRAPH_TRACER

# ifdef CONFIG_DYNAMIC_FTRACE

extern void ftrace_graph_call(void);

int ftrace_enable_ftrace_graph_caller(void)
{
	unsigned long ip = (unsigned long)&ftrace_graph_call;
	uint16_t jump_pcrel12 = ((unsigned long)&ftrace_graph_caller - ip) >> 1;
	jump_pcrel12 |= 0x2000;
	return ftrace_modify_code(ip, (void *)&jump_pcrel12, sizeof(jump_pcrel12));
}

int ftrace_disable_ftrace_graph_caller(void)
{
	return ftrace_modify_code((unsigned long)&ftrace_graph_call, empty_zero_page, 2);
}

# endif

/*
 * Hook the return address and push it in the stack of return addrs
 * in current thread info.