| /* Provide basic stack dumping functions | |
| * | |
| * Copyright 2004-2009 Analog Devices Inc. | |
| * | |
| * Licensed under the GPL-2 or later | |
| */ | |
| #include <linux/kernel.h> | |
| #include <linux/thread_info.h> | |
| #include <linux/mm.h> | |
| #include <linux/uaccess.h> | |
| #include <linux/module.h> | |
| #include <asm/trace.h> | |
| /* | |
| * Checks to see if the address pointed to is either a | |
| * 16-bit CALL instruction, or a 32-bit CALL instruction | |
| */ | |
| static bool is_bfin_call(unsigned short *addr) | |
| { | |
| unsigned int opcode; | |
| if (!get_instruction(&opcode, addr)) | |
| return false; | |
| if ((opcode >= 0x0060 && opcode <= 0x0067) || | |
| (opcode >= 0x0070 && opcode <= 0x0077) || | |
| (opcode >= 0xE3000000 && opcode <= 0xE3FFFFFF)) | |
| return true; | |
| return false; | |
| } | |
| void show_stack(struct task_struct *task, unsigned long *stack) | |
| { | |
| #ifdef CONFIG_PRINTK | |
| unsigned int *addr, *endstack, *fp = 0, *frame; | |
| unsigned short *ins_addr; | |
| char buf[150]; | |
| unsigned int i, j, ret_addr, frame_no = 0; | |
| /* | |
| * If we have been passed a specific stack, use that one otherwise | |
| * if we have been passed a task structure, use that, otherwise | |
| * use the stack of where the variable "stack" exists | |
| */ | |
| if (stack == NULL) { | |
| if (task) { | |
| /* We know this is a kernel stack, so this is the start/end */ | |
| stack = (unsigned long *)task->thread.ksp; | |
| endstack = (unsigned int *)(((unsigned int)(stack) & ~(THREAD_SIZE - 1)) + THREAD_SIZE); | |
| } else { | |
| /* print out the existing stack info */ | |
| stack = (unsigned long *)&stack; | |
| endstack = (unsigned int *)PAGE_ALIGN((unsigned int)stack); | |
| } | |
| } else | |
| endstack = (unsigned int *)PAGE_ALIGN((unsigned int)stack); | |
| printk(KERN_NOTICE "Stack info:\n"); | |
| decode_address(buf, (unsigned int)stack); | |
| printk(KERN_NOTICE " SP: [0x%p] %s\n", stack, buf); | |
| if (!access_ok(VERIFY_READ, stack, (unsigned int)endstack - (unsigned int)stack)) { | |
| printk(KERN_NOTICE "Invalid stack pointer\n"); | |
| return; | |
| } | |
| /* First thing is to look for a frame pointer */ | |
| for (addr = (unsigned int *)((unsigned int)stack & ~0xF); addr < endstack; addr++) { | |
| if (*addr & 0x1) | |
| continue; | |
| ins_addr = (unsigned short *)*addr; | |
| ins_addr--; | |
| if (is_bfin_call(ins_addr)) | |
| fp = addr - 1; | |
| if (fp) { | |
| /* Let's check to see if it is a frame pointer */ | |
| while (fp >= (addr - 1) && fp < endstack | |
| && fp && ((unsigned int) fp & 0x3) == 0) | |
| fp = (unsigned int *)*fp; | |
| if (fp == 0 || fp == endstack) { | |
| fp = addr - 1; | |
| break; | |
| } | |
| fp = 0; | |
| } | |
| } | |
| if (fp) { | |
| frame = fp; | |
| printk(KERN_NOTICE " FP: (0x%p)\n", fp); | |
| } else | |
| frame = 0; | |
| /* | |
| * Now that we think we know where things are, we | |
| * walk the stack again, this time printing things out | |
| * incase there is no frame pointer, we still look for | |
| * valid return addresses | |
| */ | |
| /* First time print out data, next time, print out symbols */ | |
| for (j = 0; j <= 1; j++) { | |
| if (j) | |
| printk(KERN_NOTICE "Return addresses in stack:\n"); | |
| else | |
| printk(KERN_NOTICE " Memory from 0x%08lx to %p", ((long unsigned int)stack & ~0xF), endstack); | |
| fp = frame; | |
| frame_no = 0; | |
| for (addr = (unsigned int *)((unsigned int)stack & ~0xF), i = 0; | |
| addr < endstack; addr++, i++) { | |
| ret_addr = 0; | |
| if (!j && i % 8 == 0) | |
| printk(KERN_NOTICE "%p:", addr); | |
| /* if it is an odd address, or zero, just skip it */ | |
| if (*addr & 0x1 || !*addr) | |
| goto print; | |
| ins_addr = (unsigned short *)*addr; | |
| /* Go back one instruction, and see if it is a CALL */ | |
| ins_addr--; | |
| ret_addr = is_bfin_call(ins_addr); | |
| print: | |
| if (!j && stack == (unsigned long *)addr) | |
| printk("[%08x]", *addr); | |
| else if (ret_addr) | |
| if (j) { | |
| decode_address(buf, (unsigned int)*addr); | |
| if (frame == addr) { | |
| printk(KERN_NOTICE " frame %2i : %s\n", frame_no, buf); | |
| continue; | |
| } | |
| printk(KERN_NOTICE " address : %s\n", buf); | |
| } else | |
| printk("<%08x>", *addr); | |
| else if (fp == addr) { | |
| if (j) | |
| frame = addr+1; | |
| else | |
| printk("(%08x)", *addr); | |
| fp = (unsigned int *)*addr; | |
| frame_no++; | |
| } else if (!j) | |
| printk(" %08x ", *addr); | |
| } | |
| if (!j) | |
| printk("\n"); | |
| } | |
| #endif | |
| } | |
| EXPORT_SYMBOL(show_stack); | |
| void dump_stack(void) | |
| { | |
| unsigned long stack; | |
| #ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON | |
| int tflags; | |
| #endif | |
| trace_buffer_save(tflags); | |
| dump_bfin_trace_buffer(); | |
| dump_stack_print_info(KERN_DEFAULT); | |
| show_stack(current, &stack); | |
| trace_buffer_restore(tflags); | |
| } | |
| EXPORT_SYMBOL(dump_stack); |