diff options
author | Paul Mundt <lethal@linux-sh.org> | 2007-11-19 19:00:41 +0900 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2008-01-28 13:18:47 +0900 |
commit | 7cfee5ac3edd3b4226018da6aad87334793cb1c5 (patch) | |
tree | fc0d75fea89fbbc4d5f6fb5afd209cd9eec2b04a /arch/sh64 | |
parent | 3eeffb32049d4b43b01211a79496cfdaac0c33ad (diff) | |
download | lwn-7cfee5ac3edd3b4226018da6aad87334793cb1c5.tar.gz lwn-7cfee5ac3edd3b4226018da6aad87334793cb1c5.zip |
sh: Move over sh64 switch_to and stack unwinder.
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh64')
-rw-r--r-- | arch/sh64/kernel/switchto.S | 198 | ||||
-rw-r--r-- | arch/sh64/kernel/unwind.c | 326 |
2 files changed, 0 insertions, 524 deletions
diff --git a/arch/sh64/kernel/switchto.S b/arch/sh64/kernel/switchto.S deleted file mode 100644 index 45b2d90eed7d..000000000000 --- a/arch/sh64/kernel/switchto.S +++ /dev/null @@ -1,198 +0,0 @@ -/* - * arch/sh64/kernel/switchto.S - * - * sh64 context switch - * - * Copyright (C) 2004 Richard Curnow - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. -*/ - - .section .text..SHmedia32,"ax" - .little - - .balign 32 - - .type sh64_switch_to,@function - .global sh64_switch_to - .global __sh64_switch_to_end -sh64_switch_to: - -/* Incoming args - r2 - prev - r3 - &prev->thread - r4 - next - r5 - &next->thread - - Outgoing results - r2 - last (=prev) : this just stays in r2 throughout - - Want to create a full (struct pt_regs) on the stack to allow backtracing - functions to work. However, we only need to populate the callee-save - register slots in this structure; since we're a function our ancestors must - have themselves preserved all caller saved state in the stack. This saves - some wasted effort since we won't need to look at the values. - - In particular, all caller-save registers are immediately available for - scratch use. - -*/ - -#define FRAME_SIZE (76*8 + 8) - - movi FRAME_SIZE, r0 - sub.l r15, r0, r15 - ! Do normal-style register save to support backtrace - - st.l r15, 0, r18 ! save link reg - st.l r15, 4, r14 ! save fp - add.l r15, r63, r14 ! setup frame pointer - - ! hopefully this looks normal to the backtrace now. - - addi.l r15, 8, r1 ! base of pt_regs - addi.l r1, 24, r0 ! base of pt_regs.regs - addi.l r0, (63*8), r8 ! base of pt_regs.trregs - - /* Note : to be fixed? - struct pt_regs is really designed for holding the state on entry - to an exception, i.e. pc,sr,regs etc. However, for the context - switch state, some of this is not required. But the unwinder takes - struct pt_regs * as an arg so we have to build this structure - to allow unwinding switched tasks in show_state() */ - - st.q r0, ( 9*8), r9 - st.q r0, (10*8), r10 - st.q r0, (11*8), r11 - st.q r0, (12*8), r12 - st.q r0, (13*8), r13 - st.q r0, (14*8), r14 ! for unwind, want to look as though we took a trap at - ! the point where the process is left in suspended animation, i.e. current - ! fp here, not the saved one. - st.q r0, (16*8), r16 - - st.q r0, (24*8), r24 - st.q r0, (25*8), r25 - st.q r0, (26*8), r26 - st.q r0, (27*8), r27 - st.q r0, (28*8), r28 - st.q r0, (29*8), r29 - st.q r0, (30*8), r30 - st.q r0, (31*8), r31 - st.q r0, (32*8), r32 - st.q r0, (33*8), r33 - st.q r0, (34*8), r34 - st.q r0, (35*8), r35 - - st.q r0, (44*8), r44 - st.q r0, (45*8), r45 - st.q r0, (46*8), r46 - st.q r0, (47*8), r47 - st.q r0, (48*8), r48 - st.q r0, (49*8), r49 - st.q r0, (50*8), r50 - st.q r0, (51*8), r51 - st.q r0, (52*8), r52 - st.q r0, (53*8), r53 - st.q r0, (54*8), r54 - st.q r0, (55*8), r55 - st.q r0, (56*8), r56 - st.q r0, (57*8), r57 - st.q r0, (58*8), r58 - st.q r0, (59*8), r59 - - ! do this early as pta->gettr has no pipeline forwarding (=> 5 cycle latency) - ! Use a local label to avoid creating a symbol that will confuse the ! - ! backtrace - pta .Lsave_pc, tr0 - - gettr tr5, r45 - gettr tr6, r46 - gettr tr7, r47 - st.q r8, (5*8), r45 - st.q r8, (6*8), r46 - st.q r8, (7*8), r47 - - ! Now switch context - gettr tr0, r9 - st.l r3, 0, r15 ! prev->thread.sp - st.l r3, 8, r1 ! prev->thread.kregs - st.l r3, 4, r9 ! prev->thread.pc - st.q r1, 0, r9 ! save prev->thread.pc into pt_regs->pc - - ! Load PC for next task (init value or save_pc later) - ld.l r5, 4, r18 ! next->thread.pc - ! Switch stacks - ld.l r5, 0, r15 ! next->thread.sp - ptabs r18, tr0 - - ! Update current - ld.l r4, 4, r9 ! next->thread_info (2nd element of next task_struct) - putcon r9, kcr0 ! current = next->thread_info - - ! go to save_pc for a reschedule, or the initial thread.pc for a new process - blink tr0, r63 - - ! Restore (when we come back to a previously saved task) -.Lsave_pc: - addi.l r15, 32, r0 ! r0 = next's regs - addi.l r0, (63*8), r8 ! r8 = next's tr_regs - - ld.q r8, (5*8), r45 - ld.q r8, (6*8), r46 - ld.q r8, (7*8), r47 - ptabs r45, tr5 - ptabs r46, tr6 - ptabs r47, tr7 - - ld.q r0, ( 9*8), r9 - ld.q r0, (10*8), r10 - ld.q r0, (11*8), r11 - ld.q r0, (12*8), r12 - ld.q r0, (13*8), r13 - ld.q r0, (14*8), r14 - ld.q r0, (16*8), r16 - - ld.q r0, (24*8), r24 - ld.q r0, (25*8), r25 - ld.q r0, (26*8), r26 - ld.q r0, (27*8), r27 - ld.q r0, (28*8), r28 - ld.q r0, (29*8), r29 - ld.q r0, (30*8), r30 - ld.q r0, (31*8), r31 - ld.q r0, (32*8), r32 - ld.q r0, (33*8), r33 - ld.q r0, (34*8), r34 - ld.q r0, (35*8), r35 - - ld.q r0, (44*8), r44 - ld.q r0, (45*8), r45 - ld.q r0, (46*8), r46 - ld.q r0, (47*8), r47 - ld.q r0, (48*8), r48 - ld.q r0, (49*8), r49 - ld.q r0, (50*8), r50 - ld.q r0, (51*8), r51 - ld.q r0, (52*8), r52 - ld.q r0, (53*8), r53 - ld.q r0, (54*8), r54 - ld.q r0, (55*8), r55 - ld.q r0, (56*8), r56 - ld.q r0, (57*8), r57 - ld.q r0, (58*8), r58 - ld.q r0, (59*8), r59 - - ! epilogue - ld.l r15, 0, r18 - ld.l r15, 4, r14 - ptabs r18, tr0 - movi FRAME_SIZE, r0 - add r15, r0, r15 - blink tr0, r63 -__sh64_switch_to_end: -.LFE1: - .size sh64_switch_to,.LFE1-sh64_switch_to - diff --git a/arch/sh64/kernel/unwind.c b/arch/sh64/kernel/unwind.c deleted file mode 100644 index 1214c78e3584..000000000000 --- a/arch/sh64/kernel/unwind.c +++ /dev/null @@ -1,326 +0,0 @@ -/* - * arch/sh64/kernel/unwind.c - * - * Copyright (C) 2004 Paul Mundt - * Copyright (C) 2004 Richard Curnow - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ -#include <linux/kallsyms.h> -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <asm/page.h> -#include <asm/ptrace.h> -#include <asm/processor.h> -#include <asm/io.h> - -static u8 regcache[63]; - -/* - * Finding the previous stack frame isn't horribly straightforward as it is - * on some other platforms. In the sh64 case, we don't have "linked" stack - * frames, so we need to do a bit of work to determine the previous frame, - * and in turn, the previous r14/r18 pair. - * - * There are generally a few cases which determine where we can find out - * the r14/r18 values. In the general case, this can be determined by poking - * around the prologue of the symbol PC is in (note that we absolutely must - * have frame pointer support as well as the kernel symbol table mapped, - * otherwise we can't even get this far). - * - * In other cases, such as the interrupt/exception path, we can poke around - * the sp/fp. - * - * Notably, this entire approach is somewhat error prone, and in the event - * that the previous frame cannot be determined, that's all we can do. - * Either way, this still leaves us with a more correct backtrace then what - * we would be able to come up with by walking the stack (which is garbage - * for anything beyond the first frame). - * -- PFM. - */ -static int lookup_prev_stack_frame(unsigned long fp, unsigned long pc, - unsigned long *pprev_fp, unsigned long *pprev_pc, - struct pt_regs *regs) -{ - const char *sym; - char namebuf[128]; - unsigned long offset; - unsigned long prologue = 0; - unsigned long fp_displacement = 0; - unsigned long fp_prev = 0; - unsigned long offset_r14 = 0, offset_r18 = 0; - int i, found_prologue_end = 0; - - sym = kallsyms_lookup(pc, NULL, &offset, NULL, namebuf); - if (!sym) - return -EINVAL; - - prologue = pc - offset; - if (!prologue) - return -EINVAL; - - /* Validate fp, to avoid risk of dereferencing a bad pointer later. - Assume 128Mb since that's the amount of RAM on a Cayman. Modify - when there is an SH-5 board with more. */ - if ((fp < (unsigned long) phys_to_virt(__MEMORY_START)) || - (fp >= (unsigned long)(phys_to_virt(__MEMORY_START)) + 128*1024*1024) || - ((fp & 7) != 0)) { - return -EINVAL; - } - - /* - * Depth to walk, depth is completely arbitrary. - */ - for (i = 0; i < 100; i++, prologue += sizeof(unsigned long)) { - unsigned long op; - u8 major, minor; - u8 src, dest, disp; - - op = *(unsigned long *)prologue; - - major = (op >> 26) & 0x3f; - src = (op >> 20) & 0x3f; - minor = (op >> 16) & 0xf; - disp = (op >> 10) & 0x3f; - dest = (op >> 4) & 0x3f; - - /* - * Stack frame creation happens in a number of ways.. in the - * general case when the stack frame is less than 511 bytes, - * it's generally created by an addi or addi.l: - * - * addi/addi.l r15, -FRAME_SIZE, r15 - * - * in the event that the frame size is bigger than this, it's - * typically created using a movi/sub pair as follows: - * - * movi FRAME_SIZE, rX - * sub r15, rX, r15 - */ - - switch (major) { - case (0x00 >> 2): - switch (minor) { - case 0x8: /* add.l */ - case 0x9: /* add */ - /* Look for r15, r63, r14 */ - if (src == 15 && disp == 63 && dest == 14) - found_prologue_end = 1; - - break; - case 0xa: /* sub.l */ - case 0xb: /* sub */ - if (src != 15 || dest != 15) - continue; - - fp_displacement -= regcache[disp]; - fp_prev = fp - fp_displacement; - break; - } - break; - case (0xa8 >> 2): /* st.l */ - if (src != 15) - continue; - - switch (dest) { - case 14: - if (offset_r14 || fp_displacement == 0) - continue; - - offset_r14 = (u64)(((((s64)op >> 10) & 0x3ff) << 54) >> 54); - offset_r14 *= sizeof(unsigned long); - offset_r14 += fp_displacement; - break; - case 18: - if (offset_r18 || fp_displacement == 0) - continue; - - offset_r18 = (u64)(((((s64)op >> 10) & 0x3ff) << 54) >> 54); - offset_r18 *= sizeof(unsigned long); - offset_r18 += fp_displacement; - break; - } - - break; - case (0xcc >> 2): /* movi */ - if (dest >= 63) { - printk(KERN_NOTICE "%s: Invalid dest reg %d " - "specified in movi handler. Failed " - "opcode was 0x%lx: ", __FUNCTION__, - dest, op); - - continue; - } - - /* Sign extend */ - regcache[dest] = - ((((s64)(u64)op >> 10) & 0xffff) << 54) >> 54; - break; - case (0xd0 >> 2): /* addi */ - case (0xd4 >> 2): /* addi.l */ - /* Look for r15, -FRAME_SIZE, r15 */ - if (src != 15 || dest != 15) - continue; - - /* Sign extended frame size.. */ - fp_displacement += - (u64)(((((s64)op >> 10) & 0x3ff) << 54) >> 54); - fp_prev = fp - fp_displacement; - break; - } - - if (found_prologue_end && offset_r14 && (offset_r18 || *pprev_pc) && fp_prev) - break; - } - - if (offset_r14 == 0 || fp_prev == 0) { - if (!offset_r14) - pr_debug("Unable to find r14 offset\n"); - if (!fp_prev) - pr_debug("Unable to find previous fp\n"); - - return -EINVAL; - } - - /* For innermost leaf function, there might not be a offset_r18 */ - if (!*pprev_pc && (offset_r18 == 0)) - return -EINVAL; - - *pprev_fp = *(unsigned long *)(fp_prev + offset_r14); - - if (offset_r18) - *pprev_pc = *(unsigned long *)(fp_prev + offset_r18); - - *pprev_pc &= ~1; - - return 0; -} - -/* Don't put this on the stack since we'll want to call sh64_unwind - * when we're close to underflowing the stack anyway. */ -static struct pt_regs here_regs; - -extern const char syscall_ret; -extern const char ret_from_syscall; -extern const char ret_from_exception; -extern const char ret_from_irq; - -static void sh64_unwind_inner(struct pt_regs *regs); - -static void unwind_nested (unsigned long pc, unsigned long fp) -{ - if ((fp >= __MEMORY_START) && - ((fp & 7) == 0)) { - sh64_unwind_inner((struct pt_regs *) fp); - } -} - -static void sh64_unwind_inner(struct pt_regs *regs) -{ - unsigned long pc, fp; - int ofs = 0; - int first_pass; - - pc = regs->pc & ~1; - fp = regs->regs[14]; - - first_pass = 1; - for (;;) { - int cond; - unsigned long next_fp, next_pc; - - if (pc == ((unsigned long) &syscall_ret & ~1)) { - printk("SYSCALL\n"); - unwind_nested(pc,fp); - return; - } - - if (pc == ((unsigned long) &ret_from_syscall & ~1)) { - printk("SYSCALL (PREEMPTED)\n"); - unwind_nested(pc,fp); - return; - } - - /* In this case, the PC is discovered by lookup_prev_stack_frame but - it has 4 taken off it to look like the 'caller' */ - if (pc == ((unsigned long) &ret_from_exception & ~1)) { - printk("EXCEPTION\n"); - unwind_nested(pc,fp); - return; - } - - if (pc == ((unsigned long) &ret_from_irq & ~1)) { - printk("IRQ\n"); - unwind_nested(pc,fp); - return; - } - - cond = ((pc >= __MEMORY_START) && (fp >= __MEMORY_START) && - ((pc & 3) == 0) && ((fp & 7) == 0)); - - pc -= ofs; - - printk("[<%08lx>] ", pc); - print_symbol("%s\n", pc); - - if (first_pass) { - /* If the innermost frame is a leaf function, it's - * possible that r18 is never saved out to the stack. - */ - next_pc = regs->regs[18]; - } else { - next_pc = 0; - } - - if (lookup_prev_stack_frame(fp, pc, &next_fp, &next_pc, regs) == 0) { - ofs = sizeof(unsigned long); - pc = next_pc & ~1; - fp = next_fp; - } else { - printk("Unable to lookup previous stack frame\n"); - break; - } - first_pass = 0; - } - - printk("\n"); - -} - -void sh64_unwind(struct pt_regs *regs) -{ - if (!regs) { - /* - * Fetch current regs if we have no other saved state to back - * trace from. - */ - regs = &here_regs; - - __asm__ __volatile__ ("ori r14, 0, %0" : "=r" (regs->regs[14])); - __asm__ __volatile__ ("ori r15, 0, %0" : "=r" (regs->regs[15])); - __asm__ __volatile__ ("ori r18, 0, %0" : "=r" (regs->regs[18])); - - __asm__ __volatile__ ("gettr tr0, %0" : "=r" (regs->tregs[0])); - __asm__ __volatile__ ("gettr tr1, %0" : "=r" (regs->tregs[1])); - __asm__ __volatile__ ("gettr tr2, %0" : "=r" (regs->tregs[2])); - __asm__ __volatile__ ("gettr tr3, %0" : "=r" (regs->tregs[3])); - __asm__ __volatile__ ("gettr tr4, %0" : "=r" (regs->tregs[4])); - __asm__ __volatile__ ("gettr tr5, %0" : "=r" (regs->tregs[5])); - __asm__ __volatile__ ("gettr tr6, %0" : "=r" (regs->tregs[6])); - __asm__ __volatile__ ("gettr tr7, %0" : "=r" (regs->tregs[7])); - - __asm__ __volatile__ ( - "pta 0f, tr0\n\t" - "blink tr0, %0\n\t" - "0: nop" - : "=r" (regs->pc) - ); - } - - printk("\nCall Trace:\n"); - sh64_unwind_inner(regs); -} - |