diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-26 19:53:12 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-26 19:53:12 -0800 |
commit | 9626357371b519f2b955fef399647181034a77fe (patch) | |
tree | 232abd741e773c7d3afb4ba6b02fcba03b82214d /arch/xtensa/oprofile/backtrace.c | |
parent | 2b37e9a28afbd11f899738e912fb4a617a74b462 (diff) | |
parent | 9cf81c759b7db1db593b2ca60b74ec350d5f9205 (diff) | |
download | op-kernel-dev-9626357371b519f2b955fef399647181034a77fe.zip op-kernel-dev-9626357371b519f2b955fef399647181034a77fe.tar.gz |
Merge tag 'xtensa-next-20130225' of git://github.com/czankel/xtensa-linux
Pull xtensa update from Chris Zankel:
"Added features:
- add support for thread local storage (TLS)
- add accept4 and finit_module syscalls
- support medium-priority interrupts
- add support for dc232c processor variant
- support file-base simulated disk for ISS simulator
Bug fixes:
- fix return values returned by the str[n]cmp functions
- avoid mmap cache aliasing
- fix handling of 'windowed registers' in ptrace"
* tag 'xtensa-next-20130225' of git://github.com/czankel/xtensa-linux:
xtensa: add accept4 syscall
xtensa: add support for TLS
xtensa: add missing include asm/uaccess.h to checksum.h
xtensa: do not enable GENERIC_GPIO by default
xtensa: complete ptrace handling of register windows
xtensa: add support for oprofile
xtensa: move spill_registers to traps.h
xtensa: ISS: add host file-based simulated disk
xtensa: fix str[n]cmp return value
xtensa: avoid mmap cache aliasing
xtensa: add finit_module syscall
xtensa: pull signal definitions from signal-defs.h
xtensa: fix ipc_parse_version selection
xtensa: dispatch medium-priority interrupts
xtensa: Add config files for Diamond 233L - Rev C processor variant
xtensa: use new common dtc rule
xtensa: rename prom_update_property to of_update_property
Diffstat (limited to 'arch/xtensa/oprofile/backtrace.c')
-rw-r--r-- | arch/xtensa/oprofile/backtrace.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/arch/xtensa/oprofile/backtrace.c b/arch/xtensa/oprofile/backtrace.c new file mode 100644 index 0000000..66f32ee --- /dev/null +++ b/arch/xtensa/oprofile/backtrace.c @@ -0,0 +1,171 @@ +/** + * @file backtrace.c + * + * @remark Copyright 2008 Tensilica Inc. + * @remark Read the file COPYING + * + */ + +#include <linux/oprofile.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <asm/ptrace.h> +#include <asm/uaccess.h> +#include <asm/traps.h> + +/* Address of common_exception_return, used to check the + * transition from kernel to user space. + */ +extern int common_exception_return; + +/* A struct that maps to the part of the frame containing the a0 and + * a1 registers. + */ +struct frame_start { + unsigned long a0; + unsigned long a1; +}; + +static void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth) +{ + unsigned long windowstart = regs->windowstart; + unsigned long windowbase = regs->windowbase; + unsigned long a0 = regs->areg[0]; + unsigned long a1 = regs->areg[1]; + unsigned long pc = MAKE_PC_FROM_RA(a0, regs->pc); + int index; + + /* First add the current PC to the trace. */ + if (pc != 0 && pc <= TASK_SIZE) + oprofile_add_trace(pc); + else + return; + + /* Two steps: + * + * 1. Look through the register window for the + * previous PCs in the call trace. + * + * 2. Look on the stack. + */ + + /* Step 1. */ + /* Rotate WINDOWSTART to move the bit corresponding to + * the current window to the bit #0. + */ + windowstart = (windowstart << WSBITS | windowstart) >> windowbase; + + /* Look for bits that are set, they correspond to + * valid windows. + */ + for (index = WSBITS - 1; (index > 0) && depth; depth--, index--) + if (windowstart & (1 << index)) { + /* Read a0 and a1 from the + * corresponding position in AREGs. + */ + a0 = regs->areg[index * 4]; + a1 = regs->areg[index * 4 + 1]; + /* Get the PC from a0 and a1. */ + pc = MAKE_PC_FROM_RA(a0, pc); + + /* Add the PC to the trace. */ + if (pc != 0 && pc <= TASK_SIZE) + oprofile_add_trace(pc); + else + return; + } + + /* Step 2. */ + /* We are done with the register window, we need to + * look through the stack. + */ + if (depth > 0) { + /* Start from the a1 register. */ + /* a1 = regs->areg[1]; */ + while (a0 != 0 && depth--) { + + struct frame_start frame_start; + /* Get the location for a1, a0 for the + * previous frame from the current a1. + */ + unsigned long *psp = (unsigned long *)a1; + psp -= 4; + + /* Check if the region is OK to access. */ + if (!access_ok(VERIFY_READ, psp, sizeof(frame_start))) + return; + /* Copy a1, a0 from user space stack frame. */ + if (__copy_from_user_inatomic(&frame_start, psp, + sizeof(frame_start))) + return; + + a0 = frame_start.a0; + a1 = frame_start.a1; + pc = MAKE_PC_FROM_RA(a0, pc); + + if (pc != 0 && pc <= TASK_SIZE) + oprofile_add_trace(pc); + else + return; + } + } +} + +static void xtensa_backtrace_kernel(struct pt_regs *regs, unsigned int depth) +{ + unsigned long pc = regs->pc; + unsigned long *psp; + unsigned long sp_start, sp_end; + unsigned long a0 = regs->areg[0]; + unsigned long a1 = regs->areg[1]; + + sp_start = a1 & ~(THREAD_SIZE-1); + sp_end = sp_start + THREAD_SIZE; + + /* Spill the register window to the stack first. */ + spill_registers(); + + /* Read the stack frames one by one and create the PC + * from the a0 and a1 registers saved there. + */ + while (a1 > sp_start && a1 < sp_end && depth--) { + pc = MAKE_PC_FROM_RA(a0, pc); + + /* Add the PC to the trace. */ + if (kernel_text_address(pc)) + oprofile_add_trace(pc); + + if (pc == (unsigned long) &common_exception_return) { + regs = (struct pt_regs *)a1; + if (user_mode(regs)) { + pc = regs->pc; + if (pc != 0 && pc <= TASK_SIZE) + oprofile_add_trace(pc); + else + return; + return xtensa_backtrace_user(regs, depth); + } + a0 = regs->areg[0]; + a1 = regs->areg[1]; + continue; + } + + psp = (unsigned long *)a1; + + a0 = *(psp - 4); + a1 = *(psp - 3); + + if (a1 <= (unsigned long)psp) + return; + + } + return; +} + +void xtensa_backtrace(struct pt_regs * const regs, unsigned int depth) +{ + if (user_mode(regs)) + xtensa_backtrace_user(regs, depth); + else + xtensa_backtrace_kernel(regs, depth); +} |