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

Commit 4e491d14 authored by Steven Rostedt's avatar Steven Rostedt Committed by Thomas Gleixner
Browse files

ftrace: support for PowerPC



This patch adds full support for ftrace for PowerPC (both 64 and 32 bit).
This includes dynamic tracing and function filtering.

Signed-off-by: default avatarSteven Rostedt <srostedt@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent e0eca07b
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -105,11 +105,12 @@ config ARCH_NO_VIRT_TO_BUS
config PPC
	bool
	default y
	select HAVE_FTRACE
	select HAVE_IDE
	select HAVE_OPROFILE
	select HAVE_KPROBES
	select HAVE_KRETPROBES
	select HAVE_LMB
	select HAVE_OPROFILE

config EARLY_PRINTK
	bool
+14 −0
Original line number Diff line number Diff line
@@ -12,6 +12,18 @@ CFLAGS_prom_init.o += -fPIC
CFLAGS_btext.o		+= -fPIC
endif

ifdef CONFIG_FTRACE
# Do not trace early boot code
CFLAGS_REMOVE_cputable.o = -pg
CFLAGS_REMOVE_prom_init.o = -pg

ifdef CONFIG_DYNAMIC_FTRACE
# dynamic ftrace setup.
CFLAGS_REMOVE_ftrace.o = -pg
endif

endif

obj-y				:= cputable.o ptrace.o syscalls.o \
				   irq.o align.o signal_32.o pmc.o vdso.o \
				   init_task.o process.o systbl.o idle.o \
@@ -78,6 +90,8 @@ obj-$(CONFIG_KEXEC) += machine_kexec.o crash.o \
obj-$(CONFIG_AUDIT)		+= audit.o
obj64-$(CONFIG_AUDIT)		+= compat_audit.o

obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o

obj-$(CONFIG_8XX_MINIMAL_FPEMU) += softemu8xx.o

ifneq ($(CONFIG_PPC_INDIRECT_IO),y)
+130 −0
Original line number Diff line number Diff line
@@ -1035,3 +1035,133 @@ machine_check_in_rtas:
	/* XXX load up BATs and panic */

#endif /* CONFIG_PPC_RTAS */

#ifdef CONFIG_FTRACE
#ifdef CONFIG_DYNAMIC_FTRACE
_GLOBAL(mcount)
_GLOBAL(_mcount)
	stwu	r1,-48(r1)
	stw	r3, 12(r1)
	stw	r4, 16(r1)
	stw	r5, 20(r1)
	stw	r6, 24(r1)
	mflr	r3
	stw	r7, 28(r1)
	mfcr	r5
	stw	r8, 32(r1)
	stw	r9, 36(r1)
	stw	r10,40(r1)
	stw	r3, 44(r1)
	stw	r5, 8(r1)
	.globl mcount_call
mcount_call:
	bl	ftrace_stub
	nop
	lwz	r6, 8(r1)
	lwz	r0, 44(r1)
	lwz	r3, 12(r1)
	mtctr	r0
	lwz	r4, 16(r1)
	mtcr	r6
	lwz	r5, 20(r1)
	lwz	r6, 24(r1)
	lwz	r0, 52(r1)
	lwz	r7, 28(r1)
	lwz	r8, 32(r1)
	mtlr	r0
	lwz	r9, 36(r1)
	lwz	r10,40(r1)
	addi	r1, r1, 48
	bctr

_GLOBAL(ftrace_caller)
	/* Based off of objdump optput from glibc */
	stwu	r1,-48(r1)
	stw	r3, 12(r1)
	stw	r4, 16(r1)
	stw	r5, 20(r1)
	stw	r6, 24(r1)
	mflr	r3
	lwz	r4, 52(r1)
	mfcr	r5
	stw	r7, 28(r1)
	stw	r8, 32(r1)
	stw	r9, 36(r1)
	stw	r10,40(r1)
	stw	r3, 44(r1)
	stw	r5, 8(r1)
.globl ftrace_call
ftrace_call:
	bl	ftrace_stub
	nop
	lwz	r6, 8(r1)
	lwz	r0, 44(r1)
	lwz	r3, 12(r1)
	mtctr	r0
	lwz	r4, 16(r1)
	mtcr	r6
	lwz	r5, 20(r1)
	lwz	r6, 24(r1)
	lwz	r0, 52(r1)
	lwz	r7, 28(r1)
	lwz	r8, 32(r1)
	mtlr	r0
	lwz	r9, 36(r1)
	lwz	r10,40(r1)
	addi	r1, r1, 48
	bctr
#else
_GLOBAL(mcount)
_GLOBAL(_mcount)
	stwu	r1,-48(r1)
	stw	r3, 12(r1)
	stw	r4, 16(r1)
	stw	r5, 20(r1)
	stw	r6, 24(r1)
	mflr	r3
	lwz	r4, 52(r1)
	mfcr	r5
	stw	r7, 28(r1)
	stw	r8, 32(r1)
	stw	r9, 36(r1)
	stw	r10,40(r1)
	stw	r3, 44(r1)
	stw	r5, 8(r1)

	LOAD_REG_ADDR(r5, ftrace_trace_function)
#if 0
	mtctr	r3
	mr	r1, r5
	bctrl
#endif
	lwz	r5,0(r5)
#if 1
	mtctr	r5
	bctrl
#else
	bl	ftrace_stub
#endif
	nop

	lwz	r6, 8(r1)
	lwz	r0, 44(r1)
	lwz	r3, 12(r1)
	mtctr	r0
	lwz	r4, 16(r1)
	mtcr	r6
	lwz	r5, 20(r1)
	lwz	r6, 24(r1)
	lwz	r0, 52(r1)
	lwz	r7, 28(r1)
	lwz	r8, 32(r1)
	mtlr	r0
	lwz	r9, 36(r1)
	lwz	r10,40(r1)
	addi	r1, r1, 48
	bctr
#endif

_GLOBAL(ftrace_stub)
	blr

#endif /* CONFIG_MCOUNT */
+62 −0
Original line number Diff line number Diff line
@@ -870,3 +870,65 @@ _GLOBAL(enter_prom)
	ld	r0,16(r1)
	mtlr    r0
        blr

#ifdef CONFIG_FTRACE
#ifdef CONFIG_DYNAMIC_FTRACE
_GLOBAL(mcount)
_GLOBAL(_mcount)
	/* Taken from output of objdump from lib64/glibc */
	mflr	r3
	stdu	r1, -112(r1)
	std	r3, 128(r1)
	.globl mcount_call
mcount_call:
	bl	ftrace_stub
	nop
	ld	r0, 128(r1)
	mtlr	r0
	addi	r1, r1, 112
	blr

_GLOBAL(ftrace_caller)
	/* Taken from output of objdump from lib64/glibc */
	mflr	r3
	ld	r11, 0(r1)
	stdu	r1, -112(r1)
	std	r3, 128(r1)
	ld	r4, 16(r11)
.globl ftrace_call
ftrace_call:
	bl	ftrace_stub
	nop
	ld	r0, 128(r1)
	mtlr	r0
	addi	r1, r1, 112
_GLOBAL(ftrace_stub)
	blr
#else
_GLOBAL(mcount)
	blr

_GLOBAL(_mcount)
	/* Taken from output of objdump from lib64/glibc */
	mflr	r3
	ld	r11, 0(r1)
	stdu	r1, -112(r1)
	std	r3, 128(r1)
	ld	r4, 16(r11)


	LOAD_REG_ADDR(r5,ftrace_trace_function)
	ld	r5,0(r5)
	ld	r5,0(r5)
	mtctr	r5
	bctrl

	nop
	ld	r0, 128(r1)
	mtlr	r0
	addi	r1, r1, 112
_GLOBAL(ftrace_stub)
	blr

#endif
#endif
+165 −0
Original line number Diff line number Diff line
/*
 * Code for replacing ftrace calls with jumps.
 *
 * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
 *
 * Thanks goes out to P.A. Semi, Inc for supplying me with a PPC64 box.
 *
 */

#include <linux/spinlock.h>
#include <linux/hardirq.h>
#include <linux/ftrace.h>
#include <linux/percpu.h>
#include <linux/init.h>
#include <linux/list.h>

#include <asm/cacheflush.h>

#define CALL_BACK		4

static unsigned int ftrace_nop = 0x60000000;

#ifdef CONFIG_PPC32
# define GET_ADDR(addr) addr
#else
/* PowerPC64's functions are data that points to the functions */
# define GET_ADDR(addr) *(unsigned long *)addr
#endif

notrace int ftrace_ip_converted(unsigned long ip)
{
	unsigned int save;

	ip -= CALL_BACK;
	save = *(unsigned int *)ip;

	return save == ftrace_nop;
}

static unsigned int notrace ftrace_calc_offset(long ip, long addr)
{
	return (int)((addr + CALL_BACK) - ip);
}

notrace unsigned char *ftrace_nop_replace(void)
{
	return (char *)&ftrace_nop;
}

notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
{
	static unsigned int op;

	addr = GET_ADDR(addr);

	/* Set to "bl addr" */
	op = 0x48000001 | (ftrace_calc_offset(ip, addr) & 0x03fffffe);

	/*
	 * No locking needed, this must be called via kstop_machine
	 * which in essence is like running on a uniprocessor machine.
	 */
	return (unsigned char *)&op;
}

#ifdef CONFIG_PPC64
# define _ASM_ALIGN	" .align 3 "
# define _ASM_PTR	" .llong "
#else
# define _ASM_ALIGN	" .align 2 "
# define _ASM_PTR	" .long "
#endif

notrace int
ftrace_modify_code(unsigned long ip, unsigned char *old_code,
		   unsigned char *new_code)
{
	unsigned replaced;
	unsigned old = *(unsigned *)old_code;
	unsigned new = *(unsigned *)new_code;
	int faulted = 0;

	/* move the IP back to the start of the call */
	ip -= CALL_BACK;

	/*
	 * Note: Due to modules and __init, code can
	 *  disappear and change, we need to protect against faulting
	 *  as well as code changing.
	 *
	 * No real locking needed, this code is run through
	 * kstop_machine.
	 */
	asm volatile (
		"1: lwz		%1, 0(%2)\n"
		"   cmpw	%1, %5\n"
		"   bne		2f\n"
		"   stwu	%3, 0(%2)\n"
		"2:\n"
		".section .fixup, \"ax\"\n"
		"3:	li %0, 1\n"
		"	b 2b\n"
		".previous\n"
		".section __ex_table,\"a\"\n"
		_ASM_ALIGN "\n"
		_ASM_PTR "1b, 3b\n"
		".previous"
		: "=r"(faulted), "=r"(replaced)
		: "r"(ip), "r"(new),
		  "0"(faulted), "r"(old)
		: "memory");

	if (replaced != old && replaced != new)
		faulted = 2;

	if (!faulted)
		flush_icache_range(ip, ip + 8);

	return faulted;
}

notrace int ftrace_update_ftrace_func(ftrace_func_t func)
{
	unsigned long ip = (unsigned long)(&ftrace_call);
	unsigned char old[4], *new;
	int ret;

	ip += CALL_BACK;

	memcpy(old, &ftrace_call, 4);
	new = ftrace_call_replace(ip, (unsigned long)func);
	ret = ftrace_modify_code(ip, old, new);

	return ret;
}

notrace int ftrace_mcount_set(unsigned long *data)
{
	unsigned long ip = (long)(&mcount_call);
	unsigned long *addr = data;
	unsigned char old[4], *new;

	/* ip is at the location, but modify code will subtact this */
	ip += CALL_BACK;

	/*
	 * Replace the mcount stub with a pointer to the
	 * ip recorder function.
	 */
	memcpy(old, &mcount_call, 4);
	new = ftrace_call_replace(ip, *addr);
	*addr = ftrace_modify_code(ip, old, new);

	return 0;
}

int __init ftrace_dyn_arch_init(void *data)
{
	/* This is running in kstop_machine */

	ftrace_mcount_set(data);

	return 0;
}
Loading