|
@@ -413,3 +413,79 @@ void unwind_backtrace(struct pt_regs *regs, struct task_struct *tsk)
|
|
pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);
|
|
pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);
|
|
|
|
|
|
if (!tsk)
|
|
if (!tsk)
|
|
|
|
+ tsk = current;
|
|
|
|
+
|
|
|
|
+ if (regs) {
|
|
|
|
+ frame.fp = regs->ARM_fp;
|
|
|
|
+ frame.sp = regs->ARM_sp;
|
|
|
|
+ frame.lr = regs->ARM_lr;
|
|
|
|
+ /* PC might be corrupted, use LR in that case. */
|
|
|
|
+ frame.pc = kernel_text_address(regs->ARM_pc)
|
|
|
|
+ ? regs->ARM_pc : regs->ARM_lr;
|
|
|
|
+ } else if (tsk == current) {
|
|
|
|
+ frame.fp = (unsigned long)__builtin_frame_address(0);
|
|
|
|
+ frame.sp = current_sp;
|
|
|
|
+ frame.lr = (unsigned long)__builtin_return_address(0);
|
|
|
|
+ frame.pc = (unsigned long)unwind_backtrace;
|
|
|
|
+ } else {
|
|
|
|
+ /* task blocked in __switch_to */
|
|
|
|
+ frame.fp = thread_saved_fp(tsk);
|
|
|
|
+ frame.sp = thread_saved_sp(tsk);
|
|
|
|
+ /*
|
|
|
|
+ * The function calling __switch_to cannot be a leaf function
|
|
|
|
+ * so LR is recovered from the stack.
|
|
|
|
+ */
|
|
|
|
+ frame.lr = 0;
|
|
|
|
+ frame.pc = thread_saved_pc(tsk);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ while (1) {
|
|
|
|
+ int urc;
|
|
|
|
+ unsigned long where = frame.pc;
|
|
|
|
+
|
|
|
|
+ urc = unwind_frame(&frame);
|
|
|
|
+ if (urc < 0)
|
|
|
|
+ break;
|
|
|
|
+ dump_backtrace_entry(where, frame.pc, frame.sp - 4);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+struct unwind_table *unwind_table_add(unsigned long start, unsigned long size,
|
|
|
|
+ unsigned long text_addr,
|
|
|
|
+ unsigned long text_size)
|
|
|
|
+{
|
|
|
|
+ unsigned long flags;
|
|
|
|
+ struct unwind_table *tab = kmalloc(sizeof(*tab), GFP_KERNEL);
|
|
|
|
+
|
|
|
|
+ pr_debug("%s(%08lx, %08lx, %08lx, %08lx)\n", __func__, start, size,
|
|
|
|
+ text_addr, text_size);
|
|
|
|
+
|
|
|
|
+ if (!tab)
|
|
|
|
+ return tab;
|
|
|
|
+
|
|
|
|
+ tab->start = (const struct unwind_idx *)start;
|
|
|
|
+ tab->stop = (const struct unwind_idx *)(start + size);
|
|
|
|
+ tab->origin = unwind_find_origin(tab->start, tab->stop);
|
|
|
|
+ tab->begin_addr = text_addr;
|
|
|
|
+ tab->end_addr = text_addr + text_size;
|
|
|
|
+
|
|
|
|
+ spin_lock_irqsave(&unwind_lock, flags);
|
|
|
|
+ list_add_tail(&tab->list, &unwind_tables);
|
|
|
|
+ spin_unlock_irqrestore(&unwind_lock, flags);
|
|
|
|
+
|
|
|
|
+ return tab;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void unwind_table_del(struct unwind_table *tab)
|
|
|
|
+{
|
|
|
|
+ unsigned long flags;
|
|
|
|
+
|
|
|
|
+ if (!tab)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ spin_lock_irqsave(&unwind_lock, flags);
|
|
|
|
+ list_del(&tab->list);
|
|
|
|
+ spin_unlock_irqrestore(&unwind_lock, flags);
|
|
|
|
+
|
|
|
|
+ kfree(tab);
|
|
|
|
+}
|