From: David VomLehn <dvomlehn@cisco.com>
To: linux-mips@linux-mips.org
Subject: [RFC] [PATCH 1/1] [MIPS] Advanced Kernel Stack Backtrace
Date: Wed, 07 May 2008 16:49:53 -0700 [thread overview]
Message-ID: <48224021.7050306@cisco.com> (raw)
This patch contains the kernel stack backtrace code we've been running
for about a year on our MIPS-based settop box. It is considerably larger
than the existing backtrace implementation and so is configurable in the
Kernel Hacking section. It also requires that KALLSYMS be enabled. In
return, I think it offers some advantages over the existing backtrace code:
o It will backtrace over nested interrupts and exceptions, allowing
detailed analysis of was going on when it was invoked.
o It handles a number of corner cases involving instructions in branch
delay slots.
o It is very careful to use __get_user when fetching stack data and
instructions, meaning that it will fail gracefully even in the presence
of stack corruption.
o It identifies whether the $sp register or another register is being
used as the frame pointer. Assuming people are happy with this
submission, there is a small subsequent patch I'll submit that dumps the
frame pointer value as part of the backtrace.
o It segregates the backtrace code into a subdirectory of
arch/mips/kernel rather than cluttering up traps.c or the kernel directory.
The main reason I am submitting this as a request for comments rather
than as a normal patch is that, though I wrote it with 64-bit systems in
mind, I don't have access to a 64-bit system on which to test it. I am
happy to merge any 64-bit-specific changes. For 32-bit systems, I'll
claim it's ready to go.
The other reason is that this is an RFC is that it is such a large,
single chunk of code that there are certainly lots of comments that
people will have. I'm not naive enough to think it's really ready
without more review.
[Note: for anyone who was at the MIPS backtrace session at the CELF
Conference, this is the code I was talking about.]
Signed-off-by: David VomLehn <dvomlehn@cisco.com>
--
diff -urN linux-2.6.25.1/arch/mips/Kconfig.debug
linux-t/arch/mips/Kconfig.debug
--- linux-2.6.25.1/arch/mips/Kconfig.debug 2008-05-01 14:45:25.000000000
-0700
+++ linux-t/arch/mips/Kconfig.debug 2008-05-06 18:21:45.000000000 -0700
@@ -73,6 +73,17 @@
include/asm-mips/debug.h for debuging macros.
If unsure, say N.
+config MIPS_ADVANCED_BACKTRACE
+ bool "More sophisticated backtrace code"
+ default n
+ depends on KALLSYMS
+ help
+ Use backtrace code that more completely handles the various
+ complexities of the MIPS processors, including branch delay
+ slots. This is substantially larger than the standard backtrace
+ code. Using this also prints the frame pointer for each function
+ in the call stack.
+
config MIPS_UNCACHED
bool "Run uncached"
depends on DEBUG_KERNEL && !SMP && !SGI_IP27
diff -urN linux-2.6.25.1/arch/mips/kernel/backtrace/kernel-backtrace.c
linux-t/arch/mips/kernel/backtrace/kernel-backtrace.c
--- linux-2.6.25.1/arch/mips/kernel/backtrace/kernel-backtrace.c
1969-12-31 16:00:00.000000000 -0800
+++ linux-t/arch/mips/kernel/backtrace/kernel-backtrace.c 2008-05-06
18:38:48.000000000 -0700
@@ -0,0 +1,978 @@
+/*
+ * kernel-backtrace.c
+ *
+ * Perform backtrace in the kernel. This means that, besides handling
signals
+ * (which can happen to kernel threads, too), it must handle
backtracing over
+ * exceptions and interrupts.
+ *
+ * Copyright (C) 2007 Scientific-Atlanta, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA
+ *
+ * Author: David VomLehn
+ */
+
+#include <linux/irq.h>
+#include <linux/kallsyms.h>
+#include <linux/module.h>
+#include <asm/system.h>
+#include <asm/mipsregs.h>
+#include <asm/uaccess.h>
+#include <asm/asm-offsets.h>
+#include <asm/kernel-backtrace.h>
+#include <asm/kernel-backtrace-symbols.h>
+
+#ifndef numberof
+#define numberof(a) (sizeof(a) / sizeof((a) [0]))
+#endif
+
+/* Offsets and sizes in the exception vector */
+#define TLB_REFILL_OFFSET 0
+#define TLB_REFILL_SIZE 0x80
+#define GEN_EX_OFFSET 0x180
+#define GEN_EX_SIZE 0x80
+#define INTERRUPT_OFFSET 0x200
+#define INTERRUPT_SIZE (NR_IRQS * vectorspacing())
+#define UNEXPECTED_OFFSET 0x80
+#define UNEXPECTED_SIZE 0x100
+
+static int in_exception_vector(reg_t pc, unsigned start, unsigned size);
+static const struct kern_special_sym *special_symbol(reg_t pc, reg_t
*start,
+ reg_t *size);
+static int pop_k0_k1_only_frame(struct kernel_bt *bt);
+static int pop_save_some_frame(struct kernel_bt *bt);
+static int pop_save_all_frame(struct kernel_bt *bt);
+static int pop_save_some_or_all_frame(struct kernel_bt *bt);
+static int pop_save_static_frame(struct kernel_bt *bt);
+static int pop_glue_frame(struct kernel_bt *bt);
+static int pop_restore_some_frame(struct kernel_bt *bt);
+static int pop_exception(struct kernel_bt *bt);
+static int do_kernel_backtrace(struct kernel_bt *bt,
+ process_kernel_frame_t process, void *arg);
+static int update_saved_registers(struct kernel_bt *bt, reg_t ip,
+ reg_t ptr);
+static int update_saved_register(struct kernel_bt *bt, reg_t ptr,
+ reg_offset_t offset);
+static int read_pt_regs(struct kernel_bt *bt);
+static int get_op(reg_t ip, opcode_t *op);
+static int get_reg(reg_t rp, reg_t *reg);
+static int symbol_lookup(reg_t ip, reg_t *start, reg_t *size);
+static int get_sc_reg(reg_t rp, reg_t *reg);
+
+static const struct thread_bt_config tb_config = {
+ {SIMPLE_BACKTRACE_LOOKUP_FUNC, get_op, get_reg, symbol_lookup},
+ get_sc_reg
+};
+
+/*
+ * Returns the number of bytes in each entry of the interrupt vector.
+ */
+static unsigned vectorspacing(void)
+{
+ const unsigned M_VS = 0x000003e0;
+ const unsigned S_VS = 0;
+
+ return(read_c0_intctl() & M_VS) >> S_VS;
+}
+
+/*
+ * Perform a stack backtrace from the values in a struct pt_regs object.
+ * Params: regs Pointer to a struct pt_regs object with the initial
+ * register values to be used for the backtrace
+ * process Function that processes a stack frame
+ * arg Argument passed to the function that processes a
+ * stack frame
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+int kernel_backtrace_pt_regs(const struct pt_regs *regs,
+ process_kernel_frame_t process, void *arg)
+{
+ struct kernel_bt bt;
+ enum reg_num i;
+
+ thread_backtrace_init(&bt.tbt, &tb_config);
+
+ for (i = REG_AT; i < REG_ALL; i ++) {
+ bt.tbt.sbt.gprs [i] = regs->regs [i];
+ bt.tbt.sbt.gpr_saved [i] = 1;
+ }
+
+ bt.tbt.sbt.pc = regs->cp0_epc;
+
+ bt.cp0_epc = regs->cp0_epc;
+ bt.cp0_status = regs->cp0_status;
+ bt.type = KERNEL_FRAME_SIMPLE;
+
+ return do_kernel_backtrace(&bt, process, arg);
+}
+EXPORT_SYMBOL(kernel_backtrace_pt_regs);
+
+/*
+ * Backtrace for a process the current function, including handling of
+ * signal frames.
+ * Params: process Function that will process each stack frame.
+ * arg Argument to passed to the processing function.
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+int kernel_backtrace(process_kernel_frame_t process, void *arg)
+{
+ struct kernel_bt bt;
+
+ thread_backtrace_init_here(&bt.tbt, &tb_config);
+
+ bt.cp0_epc = read_c0_epc();
+ bt.cp0_status = read_c0_status();
+ bt.type = KERNEL_FRAME_SIMPLE;
+
+ return do_kernel_backtrace(&bt, process, arg);
+}
+EXPORT_SYMBOL(kernel_backtrace);
+
+/*
+ * This performs a kernel backtrace.
+ * Params: bt Pointer to initialized kernel backtrace information
+ * process Function that will process each stack frame.
+ * arg Argument to passed to the processing function.
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+static int do_kernel_backtrace(struct kernel_bt *bt,
+ process_kernel_frame_t process, void *arg)
+{
+ int result;
+
+ for (result = kernel_backtrace_first(bt);
+ result == 0;
+ result = kernel_backtrace_next(bt)) {
+
+ /* If $pc is a return address, i.e. an address stored in $ra
+ * by a jalr instruction, adjust it to point to the jalr
+ * because, in some sense, that instruction is not yet
+ * complete. */
+ if (bt->tbt.pc_is_return_address) {
+ bt->tbt.sbt.pc -= 2 * OPCODE_SIZE;
+ bt_dbg(2, "$pc is return address, adjusted to 0x%lx\n",
+ bt->tbt.sbt.pc);
+ }
+
+ result = process(arg, bt);
+
+ /* If we adjusted the pc to point to a jalr instruction,
+ * restore it */
+ if (bt->tbt.pc_is_return_address)
+ bt->tbt.sbt.pc += 2 * OPCODE_SIZE;
+
+ if (result != 0)
+ break;
+ }
+
+ /* If we got a return value of -ENOSYS, the $pc and $sp values are
+ * good, but we can't continue the backtrace. Call the function to
+ * process the last frame. */
+
+ if (result == -ENOSYS) {
+ (void) process(arg, bt);
+ result = 0;
+ }
+
+ /* The result could be zero because we internally determined that
+ * the stack backtrace is done, or because the process function
+ * passed back KERNEL_BACKTRACE_END to indicate that the backtrace
+ * is done. Either case is normal, so adjust the result to indicate
+ * a normal termination. */
+ if (result > 0) {
+ bt_dbg(2, "Adjusting positive return code %d to zero", result);
+ result = 0;
+ }
+
+ return result;
+}
+
+/*
+ * Gets the first stack frame.
+ * Params: bt Pointer struct kernel_bt object
+ * Returns: KERNEL_BACKTRACE_DONE The backtrace should stop because the
+ * next frame is from user mode.
+ * Zero This is a good frame.
+ * A negative errno value The backtrace should stop because an
+ * error has occurred.
+ *
+ */
+int kernel_backtrace_first(struct kernel_bt *bt)
+{
+ int result;
+
+ result = kernel_backtrace_analyze_frame(bt);
+
+ return result;
+}
+
+/*
+ * This handles getting the next kernel stackframe. It checks to see if
we are
+ * in an exception or interrupt frame. If not, we just pass it along to the
+ * process backtracing.
+ * Params: bt Pointer to the struct kernel_bt object
+ * Returns: KERNEL_BACKTRACE_DONE The backtrace should stop because the
+ * next frame is from user mode.
+ * Zero This is a good frame.
+ * A negative errno value The backtrace should stop because an
+ * error has occurred.
+ */
+int kernel_backtrace_next(struct kernel_bt *bt)
+{
+ int result;
+
+ result = kernel_backtrace_pop_frame(bt);
+
+ if (result == 0)
+ result = kernel_backtrace_analyze_frame(bt);
+
+ return result;
+}
+
+/*
+ * This determines the type of the current frame. This is done by
looking at
+ * the $pc register and seeing whether it is in some sort of interrupt or
+ * exception frame. If not, it passes the analysis on to the
thread_backtrace
+ * code.
+ * Params: bt Pointer to a struct kernel_bt object.
+ * Returns: Zero if it was able to determine the frame type, or a negative
+ * errno value if an error occurred during processing.
+ */
+int kernel_backtrace_analyze_frame(struct kernel_bt *bt)
+{
+ int result = 0; /* Assume success */
+ reg_t pc;
+ const struct kern_special_sym *symbol;
+
+ bt->tbt.sbt.frame_size = 0;
+ pc = bt->tbt.sbt.pc;
+
+ /* The kernel is kind enough to arrange things so that the return
+ * address is NULL if we have reached the end of a kernel stack, so
+ * if we see that case, we are done. */
+ if (pc == NULL_REG)
+ result = KERNEL_BACKTRACE_DONE;
+
+ else {
+ symbol = special_symbol(pc, &bt->start, &bt->size);
+
+ if (symbol != NULL) {
+ bt->tbt.pc_is_return_address = 0;
+ bt->type = symbol->type;
+ }
+
+ else if (in_exception_vector(pc, TLB_REFILL_OFFSET,
+ TLB_REFILL_SIZE)) {
+ bt->tbt.pc_is_return_address = 0;
+ bt->type = KERNEL_FRAME_TLB_REFILL;
+ bt->start = ebase + TLB_REFILL_OFFSET;
+ bt->size = TLB_REFILL_SIZE;
+ }
+
+ else if (in_exception_vector(pc, GEN_EX_OFFSET, GEN_EX_SIZE)) {
+ bt->tbt.pc_is_return_address = 0;
+ bt->type = KERNEL_FRAME_GENERAL_EXCEPTION;
+ bt->start = ebase + GEN_EX_OFFSET;
+ bt->size = GEN_EX_SIZE;
+ }
+
+ else if (in_exception_vector(pc, INTERRUPT_OFFSET,
+ INTERRUPT_SIZE)) {
+ unsigned offset, irq, spacing;
+ bt->tbt.pc_is_return_address = 0;
+ bt->type = KERNEL_FRAME_INTERRUPT;
+
+ /* Since we are using the exception vector for handling
+ * interrupts, i.e. the CP0 Config3 register has VEIC
+ * or VI set and the CP0 Cause register has the IV bit
+ * set, we subtract ebase + INTERRUPT_OFFSET from $pc
+ * to get the offset into the interrupt vector portion.
+ * We divide this by the space of the interrupt vector
+ * entries to get the interrupt number. Then the start
+ * of the entry for this interrupt is computed by
+ * multiplying the interrupt number by the spacing and
+ * adding ebase and INTERRUPT_OFFSET back in. The size
+ * of the entry is given by the spacing of the
+ * interrupt vector entries. */
+ spacing = vectorspacing();
+ offset = pc - (ebase + INTERRUPT_OFFSET);
+ irq = offset / spacing;
+ bt->start = ebase + INTERRUPT_OFFSET + irq * spacing;
+ bt->size = spacing;
+ }
+
+ else if (in_exception_vector(pc, UNEXPECTED_OFFSET,
+ UNEXPECTED_SIZE)) {
+ bt->tbt.pc_is_return_address = 0;
+ bt->type = KERNEL_FRAME_UNEXPECTED;
+ bt->start = ebase + UNEXPECTED_OFFSET;
+ bt->size = UNEXPECTED_SIZE;
+ }
+
+ else {
+ result = thread_backtrace_analyze_frame(&bt->tbt);
+ bt->type = bt->tbt.type;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Advances to the next frame based on the analysis of the current frame
+ * done by kernel_backtrace_analyze_frame().
+ * Params: bt Pointer to struct struct kernel_bt object.
+ * Returns: KERNEL_BACKTRACE_DONE The backtrace should stop because the
+ * next frame is from user mode.
+ * Zero This is a good frame.
+ * A negative errno value The backtrace should stop because an
+ * error has occurred.
+ */
+int kernel_backtrace_pop_frame(struct kernel_bt *bt)
+{
+ int result;
+
+ switch (bt->type) {
+ case KERNEL_FRAME_SIMPLE:
+ case KERNEL_FRAME_SIGNAL:
+ case KERNEL_FRAME_RT_SIGNAL: result =
+ thread_backtrace_pop_frame(&bt->tbt);
+ break;
+
+ case KERNEL_FRAME_TLB_REFILL:
+ case KERNEL_FRAME_GENERAL_EXCEPTION:
+ case KERNEL_FRAME_K0_K1_ONLY: result = pop_k0_k1_only_frame(bt);
+ break;
+
+ case KERNEL_FRAME_INTERRUPT:
+ case KERNEL_FRAME_SAVE_SOME: result = pop_save_some_frame(bt);
+ break;
+
+ case KERNEL_FRAME_SAVE_STATIC: result = pop_save_static_frame(bt);
+ break;
+
+ case KERNEL_FRAME_SAVE_ALL: result = pop_save_all_frame(bt);
+ break;
+
+ case KERNEL_FRAME_GLUE: result = pop_glue_frame(bt);
+ break;
+
+ case KERNEL_FRAME_RESTORE_SOME: result = pop_restore_some_frame(bt);
+ break;
+
+ case KERNEL_FRAME_UNEXPECTED: result = KERNEL_BACKTRACE_DONE;
+ break;
+
+ default: result = -EINVAL; /* Internal failure: Shouldn't happen */
+ bt_dbg(1, "Unexpected frame type: %d\n", bt->type);
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * Function that determines whether we are in some section of the exception
+ * vector. If no exception vector has been set up, which we determine
+ * by seeing whether ebase has yet been set, we can't be in the exception
+ * vector code.
+ * Params: pc Current program counter
+ * start Offset of the start of the section from ebase
+ * size Size of the section.
+ * Returns: Non-zero if we are in the given section, zero otherwise.
+ */
+static int in_exception_vector(reg_t pc, unsigned start, unsigned size)
+{
+ int result;
+
+ if (ebase == 0)
+ result = 0;
+
+ else {
+ reg_t offset;
+
+ offset = pc - ebase;
+ result = (offset >= start && offset < size);
+ }
+
+ return result;
+}
+
+/*
+ * This looks for the given pc value in the list of pieces of code that
must
+ * be handled specially for backtrace purposes. If found, it will store the
+ * symbol start and size.
+ * Params: pc Value of pc to look for
+ * start Address of the start of the code section.
+ * size Number of bytes in the code section.
+ * Returns: A pointer to the entry in the table of special symbols
+ * corresponding to pc if it could be found, NULL if not.
+ */
+static const struct kern_special_sym *special_symbol(reg_t pc, reg_t
*start,
+ reg_t *size)
+{
+ const struct kern_special_sym *result;
+ unsigned long symbolsize;
+ unsigned long offset;
+
+ /* We could look up each symbol and then see whether it contained the
+ * pc, but a faster way is to look up the symbol corresponding to the
+ * pc, then just quickly go through the table looking for it. This
+ * could be even faster if the table were sorted by address because
+ * we would be able to do a binary search of the table, but this is
+ * simpler and only rarely done. */
+
+ /* Find the symbol corresponding to the pc */
+ if (!kallsyms_lookup_size_offset((unsigned long) pc, &symbolsize,
+ &offset))
+ result = NULL;
+
+ else {
+ size_t i;
+ opcode_t *symbol_start;
+
+ /* Search for the address within our table of special symbols.
+ * We do a simple linear search, now, but if the table were
+ * sorted we could use a faster binary search. Ah, someday when
+ * we have time... */
+ symbol_start = (opcode_t *) (pc - offset);
+
+ for (i = 0;
+ i < kernel_backtrace_symbols_size &&
+ symbol_start !=
+ kernel_backtrace_symbols [i].start;
+ i ++)
+ ;
+
+ if (i == kernel_backtrace_symbols_size)
+ result = NULL;
+
+ else {
+ result = &kernel_backtrace_symbols [i];
+ *start = (reg_t) symbol_start;
+ *size = symbolsize;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Loads the next register values for code that uses the $k0 and $k1
+ * registers only. In this case, the $pc value is in the CP0 EPC register
+ * and all other registers still have their original values.
+ * Params: bt Pointer to the current struct kernel_bt object.
+ * Returns: KERNEL_BACKTRACE_DONE The backtrace should stop because the
+ * next frame is from user mode.
+ * Zero This is a good frame.
+ * A negative errno value The backtrace should stop because an
+ * error has occurred.
+ */
+static int pop_k0_k1_only_frame(struct kernel_bt *bt)
+{
+ int result = 0; /* Assume success */
+
+ bt->tbt.sbt.pc = bt->cp0_epc;
+ result = pop_exception(bt);
+
+ return result;
+}
+
+/*
+ * Loads the next register values for code that uses the SAVE_SOME macro.
+ * Params: bt Pointer to the current struct kernel_bt object.
+ * Returns: KERNEL_BACKTRACE_DONE The backtrace should stop because the
+ * next frame is from user mode.
+ * Zero This is a good frame.
+ * A negative errno value The backtrace should stop because an
+ * error has occurred.
+ */
+static int pop_save_some_frame(struct kernel_bt *bt)
+{
+ return pop_save_some_or_all_frame(bt);
+}
+
+/*
+ * Loads the next register values for code that uses the SAVE_ALL
macro. The
+ * SAVE_ALL macro starts by using the SAVE_SOME macro, then saves
additional
+ * registers. We could simply have used pop_save_some_or_all_frame
directly,
+ * but this extra, tiny, function allows a more directly mapping from what
+ * appears in the kernel code to the way we break things down here.
+ * Params: bt Pointer to the current struct kernel_bt object.
+ * Returns: KERNEL_BACKTRACE_DONE The backtrace should stop because the
+ * next frame is from user mode.
+ * Zero This is a good frame.
+ * A negative errno value The backtrace should stop because an
+ * error has occurred.
+ */
+static int pop_save_all_frame(struct kernel_bt *bt)
+{
+ return pop_save_some_or_all_frame(bt);
+}
+
+/*
+ * This handles a code section that starts with use of a SAVE_SOME
macro and
+ * which *may* then save additional registers using macros like
SAVE_STATIC,
+ * etc.
+ * Params: bt Pointer to the current struct kernel_bt object.
+ * Returns: KERNEL_BACKTRACE_DONE The backtrace should stop because the
+ * next frame is from user mode.
+ * Zero This is a good frame.
+ * A negative errno value The backtrace should stop because an
+ * error has occurred.
+ */
+static int pop_save_some_or_all_frame(struct kernel_bt *bt)
+{
+ int result = 0; /* Assume success */
+ enum {OLD_SP_IN_SP, OLD_SP_IN_K0, OLD_SP_ON_STACK} sp_loc;
+ reg_t ip;
+ opcode_t op;
+
+ /* First, loop until the old stack pointer gets stored on the
+ * stack. No other registers get stored in the struct pt_regs
+ * object on the stack until after the stack pointer gets
+ * stored. */
+
+ for (ip = bt->start, sp_loc = OLD_SP_IN_SP; ;
+ ip = ip_next(ip)) {
+ /* If we reach the current execution point or we have stored
+ * the old SP on the stack, we are done. */
+ if (ip == bt->tbt.sbt.pc ||
+ sp_loc == OLD_SP_ON_STACK)
+ break;
+ result = get_op(ip, &op);
+ if (result != 0)
+ break;
+
+ if (is_move(op, REG_K0, REG_SP))
+ sp_loc = OLD_SP_IN_K0;
+
+ else if (sp_loc == OLD_SP_IN_K0 &&
+ is_sw(op, REG_K0, REG_SP))
+ sp_loc = OLD_SP_ON_STACK;
+ }
+
+ switch (sp_loc) {
+ case OLD_SP_IN_SP: /* Nothing to do */
+ break;
+ case OLD_SP_IN_K0: bt->tbt.sbt.gprs [REG_SP] =
+ bt->tbt.sbt.gprs [REG_K0];
+ bt->tbt.sbt.gpr_saved [REG_SP] = 1;
+ break;
+ case OLD_SP_ON_STACK: result = update_saved_registers(bt, ip,
+ bt->tbt.sbt.gprs [REG_SP]);
+ break;
+ default: result = -EINVAL; /* Internal failure: shouldn't happen */
+ bt_dbg(1, "Unexpected sp_loc value: %d\n", sp_loc);
+ break;
+ }
+
+ if (result == 0)
+ result = pop_exception(bt);
+
+ return result;
+}
+
+/*
+ * Loads the next register values for code that uses the SAVE_STATIC macro.
+ * Params: bt Pointer to the current struct kernel_bt object.
+ * Returns: KERNEL_BACKTRACE_DONE The backtrace should stop because the
+ * next frame is from user mode.
+ * Zero This is a good frame.
+ * A negative errno value The backtrace should stop because an
+ * error has occurred.
+ */
+static int pop_save_static_frame(struct kernel_bt *bt)
+{
+ int result;
+ reg_t pt_regs_ptr;
+
+ pt_regs_ptr = bt->tbt.sbt.gprs [REG_SP];
+
+ /* We must have already completed a SAVE_SOME macro in some previous
+ * section of code, which has saved general purpose registers zero,
+ * v0-v1, a0-a3, t9, gp, sp, and ra(r0, r2-r7, r25, r28, r29, r31),
+ * and CP0 registers Cause, EPC, and Status. We can read these values
+ * from their place on the stack. */
+ update_saved_register(bt, pt_regs_ptr, PT_R0); /* zero */
+ update_saved_register(bt, pt_regs_ptr, PT_R2); /* v0 */
+ update_saved_register(bt, pt_regs_ptr, PT_R3); /* v1 */
+ update_saved_register(bt, pt_regs_ptr, PT_R4); /* a0 */
+ update_saved_register(bt, pt_regs_ptr, PT_R5); /* a1 */
+ update_saved_register(bt, pt_regs_ptr, PT_R6); /* a2 */
+ update_saved_register(bt, pt_regs_ptr, PT_R7); /* a3 */
+ update_saved_register(bt, pt_regs_ptr, PT_R25); /* a4 */
+ update_saved_register(bt, pt_regs_ptr, PT_R28); /* gp */
+ update_saved_register(bt, pt_regs_ptr, PT_R29); /* sp */
+ update_saved_register(bt, pt_regs_ptr, PT_R31); /* ra */
+ update_saved_register(bt, pt_regs_ptr, PT_EPC); /* CP0 EPC */
+ update_saved_register(bt, pt_regs_ptr, PT_STATUS); /* CP0 Status */
+ update_saved_register(bt, pt_regs_ptr, PT_CAUSE); /* CP0 Cause */
+
+ /* Now read the registers which have been saved so far in this
+ * section of code */
+ result = update_saved_registers(bt, bt->start, pt_regs_ptr);
+
+ if (result == 0)
+ result = pop_exception(bt);
+
+ return result;
+}
+
+/*
+ * At this point the SAVE_SOME or SAVE_STATIC macro has started to save
+ * register values into a struct pt_regs object. We run through the current
+ * code looking for stores relative to the $sp register and restore values
+ * from there.
+ * Params: bt Pointer to the struct kernel_bt object
+ * ip Pointer to the first instruction to examine to see if
+ * it is a store.
+ * ptr Pointer to the struct pt_regs object in which values
+ * are stored.
+ * Returns: Zero on success, a negative errno value otherwise.
+ */
+static int update_saved_registers(struct kernel_bt *bt, reg_t ip,
+ reg_t ptr)
+{
+ int result = 0; /* Assume success */
+ opcode_t op;
+
+
+ for (;
+ ip != bt->tbt.sbt.pc &&
+ (result = get_op(ip, &op)) == 0 &&
+ !is_basic_block_end(op);
+ ip = ip_next(ip)) {
+
+ /* If this is a save, we use the offset to determine which
+ * register is being saved. Since all we want to do is to
+ * restore the register value, the offset is all we need to
+ * determine which register is to be restored. */
+ if (is_frame_save(op, REG_SP))
+ result = update_saved_register(bt, ptr,
+ frame_save_offset(op));
+ }
+
+ return result;
+}
+
+/*
+ * Gets a given general purpose register from the given memory location.
+ * Params: bt Pointer to the struct kernel_bt object in which
+ * to store the value.
+ * reg_num The particular register to store.
+ * ptr Location of the value
+ * Returns: Zero on success, a negative errno value otherwise.
+ */
+static inline int get_pt_gpr(struct kernel_bt *bt, enum reg_num reg_num,
+ reg_t ptr)
+{
+ return get_reg(ptr, &bt->tbt.sbt.gprs [reg_num]);
+}
+
+/*
+ * The current instruction is a save through the frame pointer. Retrieve
+ * the value that was saved. The offset tells us which register to
retrieve,
+ * as well as being the offset in the struct pt_regs from which to
retrieve it.
+ * Params: bt Pointer to the struct kernel_bt object
+ * ptr Pointer to the memory location where the struct
+ * pt_regs object is stored.
+ * offset Offset from the pointer to the value for the
+ * register we want to read
+ * Returns: Zero on success, a negative errno value otherwise.
+ */
+static int update_saved_register(struct kernel_bt *bt, reg_t ptr,
+ reg_offset_t offset)
+{
+ int result = 0; /* Assume success */
+ reg_t cp0_status;
+ reg_t where;
+
+ where = ptr + offset;
+
+ /* The comment by each line indicates whether the register is saved
+ * by the SAVE_SOME or SAVE_STATIC macro */
+ switch (offset) {
+ case PT_R0: result = get_pt_gpr(bt, REG_ZERO, where); /* SAVE_SOME*/
+ break;
+ case PT_R2: result = get_pt_gpr(bt, REG_V0, where); /* SAVE_SOME*/
+ break;
+ case PT_R3: result = get_pt_gpr(bt, REG_V1, where); /* SAVE_SOME*/
+ break;
+ case PT_R4: result = get_pt_gpr(bt, REG_A0, where); /* SAVE_SOME*/
+ break;
+ case PT_R5: result = get_pt_gpr(bt, REG_A1, where); /* SAVE_SOME*/
+ break;
+ case PT_R6: result = get_pt_gpr(bt, REG_A2, where); /* SAVE_SOME*/
+ break;
+ case PT_R7: result = get_pt_gpr(bt, REG_A3, where); /* SAVE_SOME*/
+ break;
+ case PT_R16: result = get_pt_gpr(bt, REG_S0, where); /* SAVE_STATIC*/
+ break;
+ case PT_R17: result = get_pt_gpr(bt, REG_S1, where); /* SAVE_STATIC*/
+ break;
+ case PT_R18: result = get_pt_gpr(bt, REG_S2, where); /* SAVE_STATIC*/
+ break;
+ case PT_R19: result = get_pt_gpr(bt, REG_S3, where); /* SAVE_STATIC*/
+ break;
+ case PT_R20: result = get_pt_gpr(bt, REG_S4, where); /* SAVE_STATIC*/
+ break;
+ case PT_R21: result = get_pt_gpr(bt, REG_S5, where); /* SAVE_STATIC*/
+ break;
+ case PT_R22: result = get_pt_gpr(bt, REG_S6, where); /* SAVE_STATIC*/
+ break;
+ case PT_R23: result = get_pt_gpr(bt, REG_S7, where); /* SAVE_STATIC*/
+ break;
+ case PT_R25: result = get_pt_gpr(bt, REG_T9, where); /* SAVE_SOME */
+ break;
+ case PT_R28: result = get_pt_gpr(bt, REG_GP, where); /* SAVE_SOME */
+ break;
+ case PT_R29: result = get_pt_gpr(bt, REG_SP, where); /* SAVE_SOME */
+ break;
+ case PT_R30: result = get_pt_gpr(bt, REG_S8, where); /* SAVE_STATIC*/
+ break;
+ case PT_R31: result = get_pt_gpr(bt, REG_RA, where); /* SAVE_SOME */
+ break;
+ case PT_EPC: result = get_reg(where, &bt->cp0_epc); /* SAVE_SOME */
+ break;
+ case PT_STATUS: result = get_reg(where, &cp0_status); /* SAVE_SOME */
+ if (result == 0)
+ bt->cp0_status = cp0_status;
+ break;
+ }
+
+ return result;
+}
+/*
+ * Loads the next register values for glue code that is used after
SAVE_SOME
+ * and SAVE_STATIC have been called and before RESTORE_SOME is called. This
+ * means that the values for the previous frame are all in a struct pt_regs
+ * object pointed to by $sp.
+ * Params: bt Pointer to the current struct kernel_bt object.
+ * Returns: KERNEL_BACKTRACE_DONE The backtrace should stop because the
+ * next frame is from user mode.
+ * Zero This is a good frame.
+ * A negative errno value The backtrace should stop because an
+ * error has occurred.
+ */
+static int pop_glue_frame(struct kernel_bt *bt)
+{
+ int result;
+
+ result = read_pt_regs(bt);
+
+ if (result == 0)
+ result = pop_exception(bt);
+
+ return result;
+}
+
+/*
+ * Loads the next register values for code that uses the RESTORE_SOME
macro.
+ * Until we reach the eret instruction, the $sp register points to a struct
+ * pt_regs object from which the values can be fetched. When we get to the
+ * eret, all registers except $pc have been loaded and we get the next $pc
+ * value from the CP0_EPC register.
+ * Params: bt Pointer to the current struct kernel_bt object.
+ * Returns: KERNEL_BACKTRACE_DONE The backtrace should stop because the
+ * next frame is from user mode.
+ * Zero This is a good frame.
+ * A negative errno value The backtrace should stop because an
+ * error has occurred.
+ */
+static int pop_restore_some_frame(struct kernel_bt *bt)
+{
+ int result;
+ opcode_t op;
+
+ /* Check to see whether we got to the eret instruction. If
+ * not, use the stack pointer to get to the save values.
+ * Otherwise, use the ones we have. */
+ result = get_op(bt->start, &op);
+
+ if (result == 0) {
+ if (!is_eret(op))
+ result = read_pt_regs(bt);
+ }
+
+ if (result == 0)
+ result = pop_exception(bt);
+
+ return result;
+}
+
+/* This is called when all of the registers, except for the $pc register,
+ * have been restored to their state prior to the exception. The
pre-exception
+ * value of the $pc register is stored in the CP0 EPC register. This
function
+ * checks the CP0 Status register's CU0 bit to find out whether we were
+ * executing kernel or user mode code before the exception. CU0 is set
if we
+ * were previously executing in user mode, clear if in user mode. If we
were
+ * executing user code, we are done. Otherwise, we need to restore the $pc
+ * value from the CP0 EPC register, and keep backtracing.
+ * Params: bt Pointer to struct kernel_bt object
+ * Return: KERNEL_BACKTRACE_DONE if we would have returned to user mode,
+ * otherwise zero.
+ */
+static int pop_exception(struct kernel_bt *bt)
+{
+ int result;
+
+ if ((bt->cp0_status & ST0_CU0) == 0)
+ result = KERNEL_BACKTRACE_DONE;
+
+ else {
+ /* The next address executed would be that stored in
+ * the CP0 EPC register. All other registers are
+ * restored */
+ bt->tbt.sbt.pc = bt->cp0_epc;
+ result = 0;
+ }
+
+ return result;
+}
+
+/*
+ * Read new values of the register from the struct pt_regs object to
which the
+ * current stack pointer points.
+ * Params: bt Points to the struct kernel_bt object to update.
+ * Returns: Zero on success, a negative errno value otherwise.
+ */
+static int read_pt_regs(struct kernel_bt *bt)
+{
+ int result;
+ reg_t pt_regs;
+ enum reg_num i;
+ reg_t cp0_status;
+
+ pt_regs = bt->tbt.sbt.gprs [REG_SP];
+
+ result = get_reg(pt_regs + offsetof(struct pt_regs, cp0_status),
+ &cp0_status);
+ if (result == 0) {
+ bt->cp0_status = cp0_status;
+ result = get_reg(pt_regs + offsetof(struct pt_regs, cp0_epc),
+ &bt->cp0_epc);
+ }
+
+ for (i = REG_AT; result == 0 && i < REG_ALL; i ++) {
+ result = get_reg(pt_regs + offsetof(struct pt_regs,
+ regs [i]), &bt->tbt.sbt.gprs [i]);
+ if (result == 0)
+ bt->tbt.sbt.gpr_saved [i] = 1;
+ }
+
+ return result;
+}
+
+/*
+ * Functions that are required by the simple-backtrace.c code but which
must
+ * be supplied by users of that code.
+ * ip_lookup - Look up the symbol start and size, given an address
+ * get_op - Get an opcode-sized element.
+ * get-reg - Get a register-sized element.
+ */
+
+/*
+ * Use kallsyms_lookup to find the symbol corresponding to a given address.
+ * All we care about for backtracing is where the section of code starts
+ * and the number of bytes in it.
+ * Params: ip Address for which to find the symbol
+ * start Pointer to location in which to store the starting
+ * address for the symbol.
+ * size Pointer to location in which to store the size of the
+ * symbol.
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+static int symbol_lookup(reg_t ip, reg_t *start, reg_t *size)
+{
+ int result;
+ const char *symname;
+ unsigned long symbolsize;
+ unsigned long offset;
+ char *modname;
+ char namebuf [KSYM_NAME_LEN + 1];
+
+ symname = kallsyms_lookup((unsigned long) ip, &symbolsize,
+ &offset, &modname, namebuf);
+
+ /* Perhaps, if we couldn't find the symbol, it is a two-instruction
+ * signal trampoline. It won't hurt to pretend because everything
+ * validates that the address from which it fetches instructions. */
+ if (symname == NULL) {
+ result = 0;
+ *start = ip;
+ *size = 2 * OPCODE_SIZE;
+ }
+
+ else {
+ result = 0;
+ *start = ip - offset;
+ *size = symbolsize;
+ }
+
+ return result;
+}
+
+/*
+ * Read an opcode-sized piece of data.
+ * Params: ip Address from which to read the opcode
+ * op Location in which to store the opcode once it has
+ * been read.
+ * Returns: Zero on success, a negative errno value otherwise.
+ */
+static int get_op(reg_t ip, opcode_t *op)
+{
+ int result;
+
+ result = __get_user(*op, (opcode_t *) ip);
+
+ return result;
+}
+
+/*
+ * Read an general purpose register-sized piece of data.
+ * Params: rp Address from which to read the data
+ * reg Location in which to store the value once it has
+ * been read.
+ * Returns: Zero on success, a negative errno value otherwise.
+ */
+static int get_reg(reg_t rp, reg_t *reg)
+{
+ int result;
+
+ result = __get_user(*reg, (reg_t *) rp);
+
+ return result;
+}
+
+/*
+ * Read an piece of data as big as is used in the struct sigcontext
registers.
+ * Params: rp Address from which to read the data
+ * reg Location in which to store the value once it has
+ * been read.
+ * Returns: Zero on success, a negative errno value otherwise.
+ */
+static int get_sc_reg(reg_t rp, reg_t *reg)
+{
+ int result;
+ unsigned long long sc_reg;
+
+ result = __get_user(sc_reg, (unsigned long long *) rp);
+
+ if (result == 0)
+ *reg = sc_reg;
+
+ return result;
+}
diff -urN
linux-2.6.25.1/arch/mips/kernel/backtrace/kernel-backtrace-symbols.c
linux-t/arch/mips/kernel/backtrace/kernel-backtrace-symbols.c
---
linux-2.6.25.1/arch/mips/kernel/backtrace/kernel-backtrace-symbols.c
1969-12-31 16:00:00.000000000 -0800
+++ linux-t/arch/mips/kernel/backtrace/kernel-backtrace-symbols.c
2008-05-06 17:30:09.000000000 -0700
@@ -0,0 +1,44 @@
+/*
+ * kernel-backtrace-symbols.c
+ *
+ * Array with backtrace symbols for the kernel;
+ *
+ * Copyright (C) 2007 Scientific-Atlanta, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA
+ *
+ * Author: David VomLehn
+ */
+
+#ifndef numberof
+#define numberof(a) (sizeof (a) / sizeof ((a) [0]))
+#endif
+
+/* Generate external references for the symbols that correspond to pieces
+ * of code that should be handled specially in order to do a proper kernel
+ * backtrace. */
+#define SPECIAL_SYMBOL(name, type) extern opcode_t name [];
+#include <asm/kernel-backtrace-symbols.h>
+#undef SPECIAL_SYMBOL
+
+/* Now generate the table for the symbols that we handle specially */
+#define SPECIAL_SYMBOL(name, type) {name, type},
+
+const struct kern_special_sym kernel_backtrace_symbols [] = {
+#include <asm/kernel-backtrace-symbols.h>
+};
+#undef SPECIAL_SYMBOL
+
+unsigned kernel_backtrace_symbols_size =
numberof(kernel_backtrace_symbols);
diff -urN linux-2.6.25.1/arch/mips/kernel/backtrace/Makefile
linux-t/arch/mips/kernel/backtrace/Makefile
--- linux-2.6.25.1/arch/mips/kernel/backtrace/Makefile 1969-12-31
16:00:00.000000000 -0800
+++ linux-t/arch/mips/kernel/backtrace/Makefile 2008-05-06
17:30:09.000000000 -0700
@@ -0,0 +1,4 @@
+# Makefile for Linux/MIPS advanced backtrace code
+
+obj-y += kernel-backtrace.o kernel-backtrace-symbols.o \
+ simple-backtrace.o thread-backtrace.o
diff -urN linux-2.6.25.1/arch/mips/kernel/backtrace/simple-backtrace.c
linux-t/arch/mips/kernel/backtrace/simple-backtrace.c
--- linux-2.6.25.1/arch/mips/kernel/backtrace/simple-backtrace.c
1969-12-31 16:00:00.000000000 -0800
+++ linux-t/arch/mips/kernel/backtrace/simple-backtrace.c 2008-05-06
17:30:09.000000000 -0700
@@ -0,0 +1,1236 @@
+/*
+ * simple-backtrace.c
+ *
+ * Implement an analysis and backtrace of stackframes. It only supports
+ * processing a single frame as multiple frame backtracing requires
operating
+ * system-depending things like signal frames and/or exception handling.
+ * It knows how to handle "o32" ABI-conformant backtraces and backtraces
+ * where a function start and size may be determined.
+ *
+ * Though this has been designed with some thought towards working in a
64-bit
+ * environment, only the 32-bit implementation is complete.
+ *
+ * Since this completely implements the ABI rules for processing a stack
+ * backtrace, without any OS dependencies, keeping this file a separate
entity
+ * will allow reuse in other situations.
+ *
+ * Copyright(C) 2007 Scientific-Atlanta, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA
+ *
+ * Author: David VomLehn
+ */
+
+#include <asm/simple-backtrace.h>
+
+#ifndef numberof
+#define numberof(a) (sizeof(a) / sizeof((a) [0]))
+#endif
+
+/* Pointers to functions to analyze the current function. This depends on
+ * the value of the struct simple_btype_t passed to the stack frame
+ * analysis functions */
+struct bt_ops {
+ int(*start_frame) (struct simple_bt *bt, reg_t ip);
+ int(*find_return) (struct simple_bt *bt);
+};
+
+static int read_reg_if_saved(struct simple_bt *bt, opcode_t op, reg_t fp);
+static int read_saved_reg(struct simple_bt *bt, opcode_t op, reg_t fp);
+static int read_saved_registers(struct simple_bt *bt, reg_t fp);
+static int start_frame(struct simple_bt *bt);
+static int find_sf_allocation(struct simple_bt *bt);
+static int analyze_procedure_prelude(struct simple_bt *bt);
+static void analyze_procedure_prelude_op(struct simple_bt *bt, opcode_t
op);
+static int analyze_return_block(struct simple_bt *bt);
+static void analyze_return_block_op(struct simple_bt *bt, reg_t ip,
+ opcode_t op);
+static void complete_analysis(struct simple_bt *bt);
+
+static int start_frame_abi(struct simple_bt *bt, reg_t ip);
+static int back_up_from_sp_decrement(struct simple_bt *bt, reg_t ip);
+static int find_return_abi(struct simple_bt *bt);
+
+static int start_frame_lookup(struct simple_bt *bt, reg_t ip);
+static int find_return_lookup(struct simple_bt *bt);
+static int find_return_lookup_bounded(struct simple_bt *bt, reg_t start,
+ reg_t end);
+
+/* Array, indexed by a struct simple_btype_t value, that holds the
+ * function analysis function pointers. */
+static struct bt_ops ops [] = {
+ {start_frame_abi, find_return_abi},
+ {start_frame_lookup, find_return_lookup},
+};
+
+#if BACKTRACE_DEBUG
+static const char *backtrace_type [] = {
+ "o32 ABI", "lookup"
+};
+#endif
+
+/*
+ * Look for a return from the current function. The search starts with the
+ * current location so that, later on, we can determine whether we are
+ * executing in a basic block that ends with a return. If there isn't
+ * a return in the current function that follows the current location, the
+ * search should look for a return before the current location.
+ *
+ * If successful, bt->fn_return will be set to the address of the
+ * "jr ra" instruction used to do the return.
+ * Params: bt Pointer to a struct simple_bt object.
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+static inline int find_return(struct simple_bt *bt)
+{
+ return ops [bt->config->type].find_return(bt);
+}
+
+/*
+ * Get an opcode_t-sized object from memory.
+ * Params: bt Pointer to the struct simple_bt object
+ * ip Address of the opcode.
+ * op Location in which to store the opcode
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+static inline int get_op(struct simple_bt *bt, reg_t ip, opcode_t *op)
+{
+ return bt->config->get_op(ip, op);
+}
+
+/*
+ * Get a register-sized(reg_t-sized) object from memory.
+ * Params: bt Pointer to the struct simple_bt object
+ * rp Address of the value.
+ * reg Location in which to store the value.
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+static inline int get_reg(struct simple_bt *bt, reg_t rp, reg_t *reg)
+{
+ return bt->config->get_reg(rp, reg);
+}
+
+/*
+ * Lookup a symbol's starting address and the size of the object, given an
+ * address within the symbol.
+ * Params: bt Pointer to the struct simple_bt object
+ * addr Address to look up
+ * symbolstart Starting address of the symbol
+ * symbolsize Number of bytes in the memory represented by
+ * the symbol.
+ * Returns: Zero on success, otherwise a negative errno value. If the
symbol
+ * couldn't be found, the value returned should be -ESRCH.
+ */
+static inline int symbol_lookup(struct simple_bt *bt, reg_t addr,
+ reg_t *symbolstart, reg_t *symbolsize)
+{
+ return bt->config->symbol_lookup(addr, symbolstart, symbolsize);
+}
+
+/*
+ * Functions that determine whether the opcode falls into a class of
+ * instructions.
+ */
+/* Is this a move to a register from the stack pointer? This could be
+ * initializing a frame pointer. */
+static inline int is_move_to_framepointer(opcode_t inst)
+{
+ struct r_format *op_p = (struct r_format *) &inst;
+ return is_addu_noreg(inst) &&
+ op_p->rs == REG_SP && op_p->rt == REG_ZERO;
+}
+
+/* Is this a move from a register to the stack pointer? If so, it's a
restore
+ * of the stack pointer from a frame pointer. */
+static inline int is_move_from_framepointer(opcode_t inst)
+{
+ struct r_format *op_p = (struct r_format *) &inst;
+ return is_addu_noreg(inst) &&
+ op_p->rt == REG_ZERO && op_p->rd == REG_SP;
+}
+
+/* Is this a decrement of the stack pointer register? */
+static inline int is_sp_decrement(opcode_t op)
+{
+ struct i_format *op_p = (struct i_format *) &op;
+ return is_addiu(op, REG_SP, REG_SP) &&
+ op_p->simmediate < 0;
+}
+
+/* Is this an increment of the stack pointer register? */
+static inline int is_sp_increment(opcode_t op)
+{
+ struct i_format *op_p = (struct i_format *) &op;
+ return is_addiu(op, REG_SP, REG_SP) &&
+ op_p->simmediate > 0;
+}
+
+/* Is this a jump through the return register $ra? */
+static inline int is_return(opcode_t op)
+{
+ return is_jr(op, REG_RA);
+}
+
+/* Is this a branch or jump instruction? This would mark the end of a basic
+ * block. Note that no coprocessor branch instructions are decoded. */
+int is_basic_block_end(opcode_t op)
+{
+ int result;
+ struct any_format *op_p = (struct any_format *) &op;
+ struct r_format *op_p_r;
+ struct i_format *op_p_i;
+ struct eret_format *op_p_eret;
+
+ switch (op_p->opcode) {
+ case j_op:
+ case beq_op:
+ case bne_op:
+ case blez_op:
+ case bgtz_op:
+ case beql_op:
+ case bnel_op:
+ case blezl_op:
+ case bgtzl_op:
+ result = 1;
+ break;
+
+ case spec_op:
+ op_p_r = (struct r_format *) &op;
+ result = (op_p_r->func == jr_op &&
+ op_p_r->rt == 0 && op_p_r->rd == 0);
+ break;
+
+ case bcond_op:
+ op_p_i = (struct i_format *) &op;
+ switch (op_p_i->rt) {
+ case bltz_op:
+ case bgez_op:
+ case bltzl_op:
+ case bgezl_op:
+ result = 1;
+ break;
+
+ default:
+ result = 0;
+ break;
+ }
+ break;
+
+ case cop0_op:
+ op_p_eret = (struct eret_format *) &op;
+ result = (op_p_eret->func == eret_op &&
+ op_p_eret->co == 1 && op_p_eret->zero == 0);
+ break;
+
+
+ default: result = 0;
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * Given an instruction that ends a basic block, as determined by the
+ * is_basic_block_end function, indicates whether the instruction has a
+ * branch delay slot or not.
+ * Params: op Instruction that ended the basic block
+ * Returns: Non-zero if the instruction has a branch delay slot and
+ * zero if it does.
+ */
+int basic_block_end_uses_BDS(opcode_t op)
+{
+ return !is_eret(op);
+}
+
+/*
+ * Initialize the given struct simple_bt object for going down the
+ * stack frames.
+ * Params: bt Pointer to the struct simple_bt object to initialize
+ * config Pointer to the configuration to use for the backtrace
+ */
+void simple_backtrace_init(struct simple_bt *bt,
+ const struct simple_bt_config *config)
+{
+ simple_backtrace_clear_saved(bt);
+ bt->config = config;
+}
+EXPORT_SYMBOL(simple_backtrace_init);
+
+/*
+ * Clear the saved bits for all general purpose registers.
+ * Params: bt Pointer to the struct simple_bt object.
+ */
+void simple_backtrace_clear_saved(struct simple_bt *bt)
+{
+ enum reg_num i;
+
+ for (i = 0; i < numberof(bt->gpr_saved); i ++)
+ bt->gpr_saved [i] = 0;
+}
+EXPORT_SYMBOL(simple_backtrace_clear_saved);
+
+/*
+ * Process the first stack frame. The register values must be set by this
+ * call.
+ * Params: bt Pointer to struct simple_bt object.
+ * Returns: Zero on success, otherwise a negative errno value.
+ * A value of -ENOSYS indicates that the $pc and $sp are valid
+ * but we can't continue the backtrace.
+ */
+int simple_backtrace_first(struct simple_bt *bt)
+{
+ int result;
+
+ result = simple_backtrace_analyze_frame(bt);
+
+ return result;
+}
+EXPORT_SYMBOL(simple_backtrace_first);
+
+/*
+ * Process one stack frame. The given register values will be updated
+ * based on the processing of the stack frame.
+ * Params: bt Pointer to copies of the register values that
+ * apply for this frame.
+ * Returns: Zero on success, otherwise a negative errno value.
+ * A value of -ENOSYS indicates that the $pc and $sp are valid
+ * but we can't continue the backtrace.
+ */
+
+int simple_backtrace_next(struct simple_bt *bt)
+{
+ int result;
+
+ result = simple_backtrace_pop_frame(bt);
+
+ if (result == 0)
+ result = simple_backtrace_analyze_frame(bt);
+
+ return result;
+}
+EXPORT_SYMBOL(simple_backtrace_next);
+
+/*
+ * Gather information about the current stack frame.
+ * Params: bt Pointer to the struct simple_bt object.
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+int simple_backtrace_analyze_frame(struct simple_bt *bt)
+{
+ int result = 0; /* Assume good backtrace */
+
+ bt_dbg(1, "type \"%s\" $pc 0x%lx $sp 0x%lx\n",
+ backtrace_type [bt->config->type], bt->pc,
+ bt->gprs [REG_SP]);
+ /* Initialize for the analysis of the current stack frame */
+ start_frame(bt);
+ simple_backtrace_clear_saved(bt);
+ bt->framepointer = REG_SP; /* Default frame pointer is $sp */
+
+ /* Find the place where we allocate the stack fame, if any */
+ result = find_sf_allocation(bt);
+
+ /* If we have not allocated a stack frame, the current stack pointer
+ * is for the caller's stack frame and the return address is still
+ * in $ra. In that case, we are done with the analysis. Otherwise,
+ * let's see whether we are using a stack frame pointer */
+ if (result == 0 && bt->frame_size != 0) {
+ result = find_return(bt);
+
+ switch (result) {
+ case 0: result = analyze_return_block(bt);
+ if (result == 0) {
+ result = analyze_procedure_prelude(bt);
+ if (result == 0)
+ complete_analysis(bt);
+ }
+ break;
+
+ case -ENOSYS: bt_dbg(1,
+ "No return found, applying heuristic\n");
+ bt->possible_framepointer = REG_S8;
+ result = analyze_procedure_prelude(bt);
+ break;
+
+ default: /* Leave the result unchanged */
+ break;
+ }
+ }
+
+ return result;
+}
+EXPORT_SYMBOL(simple_backtrace_analyze_frame);
+
+/*
+ * Locates the first instruction in the current function. We start by
+ * finding the function containing the instruction to which $pc points.
Recall
+ * that $pc is actually the return address from a call. It will normally
+ * point to the same function that contains the call, except in the case
+ * where the call is the last thing in the current function. In that
special
+ * case, $pc will actually point to the first instruction in the function
+ * after the current function. This arises when the last thing the current
+ * function did was to call a function defined with __attribute__
((noreturn)).
+ *
+ * To detect this special case, note that there are two ways that we can
+ * be doing a stack backtrace with $pc pointing to the first instruction of
+ * a function:
+ * 1. We got a signal, exception, or interrupt just before executing the
+ * first instruction of a function.
+ * 2. We called a function, with a jal or jalr instruction, that, with the
+ * instruction in its branch delay slot, immediately preceeds the
+ * function.
+ * In the first case, we called the function from somewhere else and the
+ * value in the the ra register will be something other than the value of
+ * the $pc register. In the second case, however, the ra and $pc register
+ * values will be the same. In that case, the current function is the
+ * function in which the jal or jalr instruction is located, which is two
+ * instructions before the current value of the $pc register.
+ * Params: bt Points to the struct simple_bt object. The start
+ * field will be set to the result.
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+static int start_frame(struct simple_bt *bt)
+{
+ int result;
+
+ result = ops [bt->config->type].start_frame(bt, bt->pc);
+
+ if (result == 0) {
+ bt_dbg(2, "Comparing $pc 0x%lx with function start 0x%lx and "
+ "ra 0x%lx\n", bt->pc, bt->func_start,
+ bt->gprs [REG_RA]);
+ if (bt->pc == bt->func_start &&
+ bt->pc == bt->gprs [REG_RA]) {
+ bt_dbg(2, "Detected call in previous function with "
+ "pc 0x%lx\n", bt->pc);
+ result = ops [bt->config->type].start_frame(bt,
+ ip_add(bt->pc, -2 * OPCODE_SIZE));
+ }
+ }
+
+ bt_dbg(1, "Function start detected at 0x%lx\n", bt->func_start);
+
+ return result;
+}
+
+/*
+ * Sets the state to indicate that no stack frame has been allocated.
+ * Params: bt Points to the struct simple_bt object to set
+ */
+static inline void set_no_sf_allocation(struct simple_bt *bt)
+{
+ bt->sf_allocation = NULL_REG;
+ bt->frame_size = 0;
+}
+
+/*
+ * Sets the state that records where the stack frame is allocated and
+ * the frame size.
+ * Params: bt Points to the struct simple_bt object to set
+ * ip Location where the stack frame is allocated
+ * op Opcode used to allocate the stack frame, from which
+ * the size will be taken.
+ */
+static inline void set_sf_allocation(struct simple_bt *bt, reg_t ip,
+ opcode_t op)
+{
+ struct i_format *op_p;
+
+ bt->sf_allocation = ip;
+ op_p = (struct i_format *) &op;
+ bt->frame_size = -op_p->simmediate;
+ bt_dbg(2, "Found frame allocation for %d bytes at 0x%lx\n",
+ bt->frame_size, ip);
+}
+
+
+/*
+ * Finds the location where the stack frame is allocated. If one was found,
+ * the location is stored in sf_allocation, otherwise sf_allocation is set
+ * to NULL_REG.
+ * Params: bt Points to the struct simple_bt object. The start
+ * field will be set to the result.
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+static int find_sf_allocation(struct simple_bt *bt)
+{
+ int result = 0;
+ reg_t ip;
+ opcode_t op;
+
+ /* Scan forwards from the start of the function until one of the
+ * following:
+ * 1. We reach the current execution location, in which case
+ * no stack frame has been allocated,
+ * 2. We fail to read an opcode, which is an error,
+ * 3. We see a stack frame decrement instruction, which is how
+ * the stack frame is allocated, or
+ * 4. We reach an instruction marking the end of a basic block,
+ * in which the function does not allocate a stack frame at all.
+ */
+ for (ip = bt->func_start; ; ip = ip_next(ip)) {
+ if (ip == bt->pc)
+ break;
+
+ result = get_op(bt, ip, &op);
+
+ if (result != 0 ||
+ is_sp_decrement(op) ||
+ is_basic_block_end(op))
+ break;
+
+ bt_dbg(3, "Looked at 0x%lx, opcode 0x%x\n", ip, op);
+ }
+
+ if (ip == bt->pc)
+ set_no_sf_allocation(bt);
+
+ else if (result == 0) {
+ if (is_sp_decrement(op))
+ set_sf_allocation(bt, ip, op);
+
+ else {
+ /* We exited the loop because we found the end of
+ * the basic block. If the instruction that marked
+ * the end of the basic block uses a branch delay
+ * slot, we need to examine that instruction to see
+ * if it allocated a stack frame. */
+
+ ip = ip_next(ip); /* Adv. to branch delay slot */
+ result = get_op(bt, ip, &op);
+
+ if (result == 0) {
+ if (is_sp_decrement(op))
+ set_sf_allocation(bt, ip, op);
+
+ else
+ set_no_sf_allocation(bt);
+ }
+ }
+ }
+
+ return result;
+}
+
+/*
+ * This function analyzes a basic block ending with the function return
+ * at fn_return. We do a backwards scan, starting with the "jr ra"
instruction
+ * until we reach the end of the previous basic block, or the stack frame
+ * allocation, whichever comes first.
+ * Params: bt Pointer to the struct simple_bt object.
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+static int analyze_return_block(struct simple_bt *bt)
+{
+ int result;
+ reg_t ip;
+ opcode_t op;
+
+ bt->possible_framepointer = REG_SP;
+ bt->fp_restore = NULL_REG;
+ bt->sf_deallocation = NULL_REG;
+
+ /* Doing this analysis is a bit complicated because of branch delay
+ * slots. The first complication is that the first instruction in
+ * our backwards scan is the one after the "jr ra" instruction. The
+ * next complication is that the $pc value associated with instruction
+ * in the branch delay slot is actually that of the "jr ra"
+ * instruction. Either they are both executed or neither are
+ * executed. */
+ ip = bt->fn_return;
+ result = get_op(bt, ip_next(ip), &op);
+
+ if (result == 0) {
+ opcode_t prev_op;
+ reg_t prev_ip;
+
+ analyze_return_block_op(bt, ip, op);
+
+ /* The next complication in the backwards scan is that we
+ * don't want to evaluate the instruction after the one that
+ * marks the end of the previous block if it is in a branch
+ * delay slot. So, we start by getting an opcode, which we
+ * refer to as the current op code. Then, until:
+ * 1. We have reached the instruction where the stack
+ * frame was allocated,
+ * 2. We failed to read the next opcode, and
+ * 3. The opcode we got marks the end of a basic block,
+ * we analyze the current opcode. After analyzing the current
+ * opcode, the next opcode becomes the current opcode and
+ * we loop again. */
+ result = get_op(bt, bt->fn_return, &op);
+
+ if (result == 0) {
+ for (prev_ip = ip_prev(ip); ; prev_ip = ip_prev(ip)) {
+ if (prev_ip == bt->sf_allocation)
+ break;
+
+ result = get_op(bt, prev_ip, &prev_op);
+
+ if (result != 0 ||
+ is_basic_block_end(prev_op))
+ break;
+
+ analyze_return_block_op(bt, ip, op);
+ ip = prev_ip;
+ op = prev_op;
+ }
+
+ /* If we stopped because prev_op was the instruction
+ * that marked the end of the previous block, and that
+ * instruction does not have a branch delay slot, we
+ * still have to analyze the current instruction */
+ if (prev_ip == bt->sf_allocation ||
+ (result == 0 && is_basic_block_end(prev_op) &&
+ !basic_block_end_uses_BDS(prev_op)))
+ analyze_return_block_op(bt, ip, op);
+ }
+ }
+
+ if (result == 0 && bt->sf_deallocation == NULL_REG) {
+ bt_dbg(1, "Stack frame not deallocated\n");
+ result = -ENOSYS;
+ }
+
+ return result;
+}
+
+/*
+ * This looks at the given opcode, which comes from the given address,
to see
+ * if it is a move to the stack pointer or a stack pointer increment.
If so,
+ * it records that information.
+ *
+ * We assume that the basic block that includes the function return
instruction
+ * can have only one move to the $sp register, but don't check for that
+ * fact. We also don't check that the frame pointer restore preceeds the
+ * stack frame deallocation.
+ *
+ * This may set the following struct simple_bt fields:
+ * possible_framepointer Register number used in "move sp, rx"
+ * instruction.
+ * fp_restore Address of the "move sp, rx" instruction
+ * sf_deallocation Address of "addiu sp, sp, framesize" instruction
+ *
+ * Params: bt Pointer to the struct simple_bt object.
+ * ip Address of the instruction being analyzed
+ * op Instruction to analyze
+ */
+static void analyze_return_block_op(struct simple_bt *bt, reg_t ip,
+ opcode_t op)
+{
+ bt_dbg(3, "analyze opcode 0x%08x at 0x%lx\n", op, ip);
+
+ if (is_move_from_framepointer(op)) {
+ struct r_format *op_p = (struct r_format *) &op;
+ bt->possible_framepointer = op_p->rs;
+ bt->fp_restore = ip;
+ bt_dbg(3, "possible frame pointer $r%d at 0x%lx\n",
+ bt->possible_framepointer, ip);
+ }
+
+ else if (is_sp_increment(op)) {
+ bt->sf_deallocation = ip;
+ bt_dbg(2, "frame deallocation at 0x%lx\n", ip);
+ }
+}
+
+/*
+ * This function analyzes the procedure prelude, which is the first basic
+ * in the function, to see if a frame pointer has been established. It
stops
+ * when it sees a frame pointer established, it reaches the end of the
basic
+ * block, or when when it reaches $pc. This function assumes that the
+ * framepointer element of the struct simple_bt has already been set to
+ * REG_SP.
+ * Params: bt Pointer to the struct simple_bt object.
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+static int analyze_procedure_prelude(struct simple_bt *bt)
+{
+ int result = 0; /* Assume good result */
+ reg_t ip;
+ opcode_t op;
+
+ /* Starting at the instruction after the allocation of the stack
+ * frame, look at each opcode to see if a frame pointer has been
+ * established. Continue looking until:
+ * 1. We have reached the instruction about to be executed,
+ * 2. We have established a frame pointer,
+ * 3. We aren't able to read the next opcode, and
+ * 4. The opcode we read marks the end of a basic block. */
+ for (ip = ip_next(bt->sf_allocation); ; ip = ip_next(ip)) {
+ if (ip == bt->pc ||
+ bt->framepointer != REG_SP)
+ break;
+ result = get_op(bt, ip, &op);
+ if (result != 0 ||
+ is_basic_block_end(op))
+ break;
+ bt_dbg(3, "analyze opcode 0x%08x at 0x%lx\n", op, ip);
+ analyze_procedure_prelude_op(bt, op);
+ }
+
+ /* If we did not reach the current executation address, have not
+ * yet found a frame pointer established, are at the end of the
+ * basic block and the instruction that ended the basic block has
+ * a branch delay slot, then we need to look at the instruction in
+ * the branch delay slot to see whether it establishes a frame
+ * pointer. */
+ if (ip != bt->pc && bt->framepointer == REG_SP &&
+ is_basic_block_end(op) && basic_block_end_uses_BDS(op)) {
+ ip = ip_next(ip);
+ result = get_op(bt, ip, &op);
+
+ if (result == 0) {
+ bt_dbg(3, "analyze opcode 0x%08x at 0x%lx\n", op, ip);
+ analyze_procedure_prelude_op(bt, op);
+ }
+ }
+
+
+ return result;
+}
+
+/*
+ * This looks at the given opcode, which comes from the given address,
to see
+ * if it is a move from the stack pointer to another register. If this
matches
+ * a move from the same register to the stack pointer in the return block,
+ * this must be initialization of a frame pointer.
+ *
+ * This might set the following struct simple_bt fields:
+ * framepointer Register being used as a frame pointer.
+ *
+ * Params: bt Pointer to struct simple_bt object
+ * op Instruction to examine.
+ */
+static void analyze_procedure_prelude_op(struct simple_bt *bt, opcode_t op)
+{
+ if (is_move_to_framepointer(op)) {
+ enum reg_num rd;
+ struct r_format *op_p = (struct r_format *) &op;
+ rd = op_p->rd;
+ bt_dbg(3, "Checking $r%d to see if is a frame pointer\n", rd);
+ if (rd == bt->possible_framepointer) {
+ bt_dbg(2, "Confirmed frame pointer $r%d\n", rd);
+ bt->framepointer = rd;
+ }
+ }
+}
+
+/*
+ * This finishes the analysis of this stack frame, based on the analysis
+ * that has already been done. It assumes that:
+ * o The stack frame size is correct, if one has been allocated, or zero
+ * if one has not been allocated.
+ * o A basic block ending with a function return instruction, "jr ra", has
+ * been found, the stack frame deallocation has been located, and, if
+ * a stack frame pointer is in use, the location where it is transferred
+ * to $sp has been found.
+ * Params: bt Pointer to the struct simple_bt object used to keep
+ * track of the analysis.
+ */
+static void complete_analysis(struct simple_bt *bt)
+{
+ if (bt->frame_size != 0) {
+ /* If a stack frame pointer has been allocated, have we
+ * executed the instruction where it is moved back to $sp
+ * on the way to executing a return? */
+ if (bt->framepointer != REG_SP &&
+ bt->pc > bt->fp_restore &&
+ bt->pc <= bt->fn_return) {
+ bt_dbg(2, "After stack frame restored to $sp\n");
+ bt->framepointer = REG_SP;
+ }
+
+ /* If we have passed the point where the stack frame is
+ * deallocated on our way to a return from the function,
+ * the frame size is now effectively zero. */
+ if (bt->pc > bt->sf_deallocation &&
+ bt->pc <= bt->fn_return) {
+ bt_dbg(2, "After stack frame deallocated\n");
+ bt->frame_size = 0;
+ }
+ }
+}
+
+/*
+ * Updates the $pc and $sp registers, given the current values of the
+ * $ra register and the size of the stack frame.
+ * Params: bt Pointer to the struct simple_bt object.
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+int simple_backtrace_pop_frame(struct simple_bt *bt)
+{
+ int result = 0; /* Assume success */
+ reg_t new_sp;
+ reg_t fp;
+
+ bt_dbg(2, "Popping %d-byte frame\n", bt->frame_size);
+ /* Assuming we successfully retrieved the saved register
+ * values, the new stack pointer value is the frame pointer
+ * plus the size of the stack frame. */
+ bt_dbg(3, "framepointer in $r%d = 0x%lx\n", bt->framepointer,
+ bt->gprs [bt->framepointer]);
+ fp = bt->gprs [bt->framepointer];
+ new_sp = fp + bt->frame_size;
+
+ /* If we allocated a stack frame, read the saved registers */
+ if (bt->frame_size != 0)
+ result = read_saved_registers(bt, fp);
+
+ if (result == 0) {
+ bt->gprs [REG_SP] = new_sp;
+
+ /* Set the pc to the address of the next instruction to
+ * execute. Note that this is two instructions beyond the
+ * 'jalr' instruction used to call the function for this
+ * frame. */
+ bt->pc = bt->gprs [REG_RA];
+ }
+
+ return result;
+}
+EXPORT_SYMBOL(simple_backtrace_pop_frame);
+
+/*
+ * Update all of the registers saved within the current basic block. We
+ * go from the starting location up to the last instruction executed.
+ * Params: bt Pointer to the object in which values will be held.
+ * fp Location of the stack frame
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+static int read_saved_registers(struct simple_bt *bt, reg_t fp)
+{
+ int result = 0; /* No errors so far */
+ opcode_t op;
+ reg_t ip;
+
+ /* Loop through the code, starting with the first instruction after
+ * the stack frame allocation, and each time a register is saved
+ * in the stack frame, read its value into the array with the
+ * general purpose register values. We keep doing this until:
+ * 1. We have reached the instruction we are about to execute,
+ * 2. We can't read the next opcode, and
+ * 3. The opcode we read marks the end of a basic block. */
+ for (ip = ip_next(bt->sf_allocation); ; ip = ip_next(ip)) {
+ if (result != 0 ||
+ ip == bt->pc)
+ break;
+ result = get_op(bt, ip, &op);
+ if (result != 0 ||
+ is_basic_block_end(op))
+ break;
+ bt_dbg(3, "Check 0x%lx for register save\n", ip);
+ result = read_reg_if_saved(bt, op, fp);
+ }
+
+ /* If we reached the end of the basic block without error, and
+ * the instruction that marked the end of the basic block uses
+ * a branch delay slot, check the instruction in the branch delay
+ * slot. */
+ if (result == 0 &&
+ ip != bt->pc &&
+ basic_block_end_uses_BDS(op)) {
+ ip = ip_next(ip);
+ result = get_op(bt, ip, &op);
+
+ if (result == 0) {
+ bt_dbg(3, "Check 0x%lx for register save\n", ip);
+ result = read_reg_if_saved(bt, op, fp);
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Look at the given op code and, if it is a save of a register through
+ * the frame pointer, get the value. This is only done the first time
+ * the save occurs. Subsequent stores can't be a save of the value with
+ * which we were called.
+ * Params: bt Pointer to struct simple_bt object
+ * op Opcode to analyze.
+ * fp Value of frame pointer to use.
+ * Returns: Zero on success, otherwise a negative errno value.
+ *
+ */
+static int read_reg_if_saved(struct simple_bt *bt, opcode_t op, reg_t fp)
+{
+ int result = 0; /* No errors so far */
+
+ /* If the given instruction is a save through the
+ * frame pointer, retrieve the value from the stack.
+ *
+ * Note: the o32 ABI says that the registers will be
+ * saved through the frame pointer, but the reality is
+ * that they are saved through the stack pointer.
+ * Accomodate both. */
+
+ if (is_frame_save(op, bt->framepointer))
+ result = read_saved_reg(bt, op, fp);
+
+ else if (is_frame_save(op, REG_SP))
+ result = read_saved_reg(bt, op, fp);
+
+ return result;
+}
+
+/*
+ * If the register is a saved register, read the value from the stack
frame.
+ * Params: bt Pointer to struct simple_bt object.
+ * op Opcode used to save the register through the given
+ * frame pointer
+ * fp Value of frame pointer
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+static int read_saved_reg(struct simple_bt *bt, opcode_t op, reg_t fp)
+{
+ int result = 0; /* Assume success */
+ enum reg_num rt;
+ reg_offset_t offset;
+
+ rt = frame_save_rt(op);
+ offset = frame_save_offset(op);
+
+ /* Only worry about the first save. Subsequent saves in this
+ * function would not be saves of the caller's values for this
+ * register. */
+ switch (rt) {
+ case REG_S0:
+ case REG_S1:
+ case REG_S2:
+ case REG_S3:
+ case REG_S4:
+ case REG_S5:
+ case REG_S6:
+ case REG_S7:
+ case REG_GP:
+ case REG_S8:
+ case REG_RA: if (!bt->gpr_saved [rt]) {
+ reg_t rp;
+ rp = fp + offset;
+ result = get_reg(bt, rp, &bt->gprs [rt]);
+ bt_dbg(3, "Read $r%d from 0x%lx+%d(0x%lx)=0x%lx\n",
+ rt, fp, offset, rp, bt->gprs [rt]);
+ bt->gpr_saved [rt] = 1;
+ }
+
+ else
+ bt_dbg(3, "$r%d already read\n", rt);
+ break;
+
+ default: /* Ignore other registers */
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * Find the beginning of the function in which the given address is found.
+ * Params: bt Points to the struct simple_bt object
+ * addr Address for which to locate the function start
+ * Returns: Zero on success, otherwise:
+ * -ESRCH The given address is not in known code(inherited from
+ * symbol_lookup)
+ */
+static int start_frame_abi(struct simple_bt *bt, reg_t addr)
+{
+ int result;
+ reg_t ip;
+ opcode_t op;
+
+ /* Scan backwards until:
+ * 1. We can't read an instruction, which we take to indicate we
+ * went one instruction past the beginning of the function,
+ * 2. We found a "jr ra", which marks the end of the previous
+ * function, or
+ * 3. We found a decrement of the sp register, which is the stack
+ * frame allocation for this function.
+ */
+ for (ip = ip_prev(addr); ; ip = ip_prev(ip)) {
+ result = get_op(bt, ip, &op);
+ if (result != 0 ||
+ is_return(op) ||
+ is_sp_decrement(op))
+ break;
+ }
+
+ switch (result) {
+ case -EFAULT: bt->func_start = ip_next(ip);
+ bt_dbg(2, "Found function start at 0x%lx by running out of "
+ "code\n", bt->func_start);
+ break;
+ case 0: if (is_return(op)) {
+ bt->func_start = ip_add(ip, 2 * OPCODE_SIZE);
+ bt_dbg(2, "Found function start at 0x%lx by "
+ "finding previous function end at 0x%lx\n",
+ bt->func_start, ip);
+ }
+
+ else
+ result = back_up_from_sp_decrement(bt, ip);
+ break;
+ default: /* Got some other error */
+ bt->func_start = NULL_REG;
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * Matches the code fragment:
+ * li gp,<value>
+ * addu gp, gp, t9
+ * which is a short version of the code used to recover the GOT (global
offset
+ * table) pointer from the address of the current function.
+ * Params: op1 Possible match to: addu gp, gp, t9
+ * op2 Possible match to: li gp,<value>
+ * Returns: Non-zero if it matches, zero if it does not.
+ */
+static inline int match_short_get_got(opcode_t op1, opcode_t op2)
+{
+ return is_addu(op1, REG_GP, REG_GP, REG_T9) &&
+ is_li(op2, REG_GP);
+}
+
+/*
+ * Matches the code fragment:
+ * lui gp,%hi(<value>)
+ * addiu gp,gp,%lo(<value>)
+ * addu gp, gp, t9
+ * which is a long version of the code used to recover the GOT (global
offset
+ * table) pointer from the address of the current function.
+ * Params: op1 Possible match to: addu gp, gp, t9
+ * op2 Possible match to: addiu gp,gp,%lo(<value>)
+ * op3 Possible match to: lui gp,%hi(<value>)
+ * Returns: Non-zero if it matches, zero if it does not.
+ */
+static inline int match_long_get_got(opcode_t op1, opcode_t op2,
opcode_t op3)
+{
+ return is_addu(op1, REG_GP, REG_GP, REG_T9) &&
+ is_addiu(op2, REG_GP, REG_GP) &&
+ is_lui(op2, REG_GP);
+}
+
+/*
+ * We are trying to find the beginning of a MIPS o32 ABI conformant
function
+ * and found a stack pointer decrement at the given address. The means we
+ * must be in the first basic block of the current function. We want to
+ * keep backing up until we reach the beginning of the current function.
+ *
+ * The stack pointer decrement may be the first instruction of this
+ * function, but there is the possibility that we have code that
+ * is used to get the address of the GOT (global offset table) into
+ * the gp register. We look to see if we have such code and, if so,
+ * assume that the beginning of that code is the beginning of this
+ * function. This code will look like:
+ * la gp,<value>
+ * addu gp,gp,t9
+ * where la might expand into either:
+ * lui gp,%hi(<value>)
+ * addiu gp,gp,%lo(<value>)
+ * or, if <value> fits, as a signed value, in 16 bits:
+ * li gp,<value> (implement as addiu gp,zero,<value>)
+ *
+ * Params: bt Pointer to struct simple_bt object.
+ * sp_dec Location of stack pointer decrement instruction
+ * Returns: Zero if we found the start of the function and a negative
+ * errno value if not.
+ */
+static int back_up_from_sp_decrement(struct simple_bt *bt, reg_t sp_dec)
+{
+ int result = 0;
+ reg_t ip;
+ unsigned i;
+ opcode_t op [3];
+
+ /* Start by trying to read the three previous instructions. */
+ for (i = 0, ip = ip_prev(sp_dec);
+ i < numberof(op);
+ i ++, ip = ip_prev(ip)) {
+ result = get_op(bt, ip, &op [i]);
+ if (result != 0)
+ break;
+ }
+
+ /* If we got an error other than -EFAULT, we have a problem we should
+ * propagate. Otherwise, let's look at what we got. */
+ if (result == 0 || result == -EFAULT) {
+ switch (i) {
+ case 0: /* Only read none or one instruction */
+ case 1: bt->func_start = sp_dec;
+ result = 0;
+ break;
+
+ case 2: if (match_short_get_got(op [0], op [1])) {
+ bt_dbg(3, "Matched short get GOT code\n");
+ bt->func_start = ip_add(sp_dec,
+ -2 * OPCODE_SIZE);
+ } else
+ bt->func_start = sp_dec;
+
+ result = 0;
+ break;
+ case 3: if (match_short_get_got(op [0], op [1])) {
+ bt_dbg(3, "Matched short get GOT code\n");
+ bt->func_start = ip_add(sp_dec,
+ -2 * OPCODE_SIZE);
+ } else if (match_long_get_got(op [0], op [1], op [2])) {
+ bt_dbg(3, "Matched long get GOT code\n");
+ bt->func_start = ip_add(sp_dec,
+ -3 * OPCODE_SIZE);
+ } else
+ bt->func_start = sp_dec;
+
+ result = 0;
+ break;
+ default: result = -EINVAL; /* Internal error */
+ break;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Find the end of the function by o32 ABI rules. If the function was
not found,
+ * bt->fn_return must be set to NULL_REG.
+ * Params: bt Pointer to struct simple_bt object.
+ * Returns: Zero if we found the end of the function, a negative errno
value
+ * if we could not.
+ */
+static int find_return_abi(struct simple_bt *bt)
+{
+ int result;
+ reg_t ip;
+ opcode_t op;
+
+ /* Scan forward to find the "jr ra" that marks the end
+ * of the current function */
+ for (ip = bt->pc; ; ip = ip_next(ip)) {
+ result = get_op(bt, ip, &op);
+ if (result != 0 || is_return(op))
+ break;
+ }
+
+ if (result == 0) {
+ bt_dbg(2, "Function return at 0x%lx\n", ip);
+ bt->fn_return = ip;
+ }
+
+ return result;
+}
+
+/*
+ * Find the beginning of the function in which the given address is found.
+ * Params: bt Points to the struct simple_bt object
+ * addr Address for which to locate the function start
+ * Returns: Zero on success, otherwise:
+ * -ESRCH The given address is not in known code(inherited from
+ * symbol_lookup)
+ */
+static int start_frame_lookup(struct simple_bt *bt, reg_t addr)
+{
+ int result;
+
+ result = symbol_lookup(bt, addr, &bt->func_start,
+ &bt->data.lookup.func_size);
+
+ return result;
+}
+
+/*
+ * Find the return instruction starting at the current instruction, but if
+ * we don't find one by the end of the function, try again at the top.
+ * Params: bt Pointer to a struct simple_bt object
+ * Returns: Zero if successful, otherwise a negative errno value.
+ */
+static int find_return_lookup(struct simple_bt *bt)
+{
+ int result;
+ reg_t end;
+
+ /* Start the search at the next instruction to be excuted and search to
+ * the end */
+ end = bt->func_start + bt->data.lookup.func_size;
+ result = find_return_lookup_bounded(bt, bt->pc, end);
+
+ /* Start by scanning forward to find the "jr ra" that marks the end
+ * of the current function. If we didn't get an error, but didn't
+ * find the instruction, try again from the beginning. */
+
+ if (result == 0 && bt->fn_return == NULL_REG) {
+ /* We couldn't find a "jr ra" after the current
+ * location, let's try for one before it. We know that
+ * it can't be before the stack allocation and the
+ * stack allocation isn't a return, so start right
+ * after that.*/
+ result = find_return_lookup_bounded(bt,
+ ip_next(bt->sf_allocation), bt->pc);
+
+ /* If we failed, return an error code */
+ if (result == 0 && bt->fn_return == NULL_REG) {
+ bt_dbg(1, "No function return found\n");
+ bt_dbg(2, "1st search: 0x%lx to 0x%lx, "
+ "2nd: 0x%lx to 0x%lx\n", bt->pc, end,
+ ip_next(bt->sf_allocation), bt->pc);
+
+ result = -ENOSYS;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * This starts at the given address and looks for a function return. If
+ * it finds one, it sets the fn_return field to its address, otherwise it
+ * sets it to NULL_REG.
+ * Params: bt Points to the current struct simple_bt object
+ * start Starting address.
+ * end One instruction past the last instruction to check.
+ * The address here should not be checked.
+ * Returns: Zero if no error occurred during the scan, otherwise a
+ * negative errno value.
+ */
+static int find_return_lookup_bounded(struct simple_bt *bt, reg_t start,
+ reg_t end)
+{
+ int result = 0; /* Assume success */
+ reg_t ip;
+ opcode_t op;
+
+ for (ip = start; ; ip = ip_next(ip)) {
+ if (ip == end)
+ break;
+ result = get_op(bt, ip, &op);
+ if (result != 0 || is_return(op))
+ break;
+ }
+
+ if (result == 0) {
+ if (ip == end)
+ bt->fn_return = NULL_REG;
+
+ else {
+ bt_dbg(2, "Function return at 0x%lx\n", ip);
+ bt->fn_return = ip;
+ }
+ }
+
+ return result;
+}
diff -urN linux-2.6.25.1/arch/mips/kernel/backtrace/thread-backtrace.c
linux-t/arch/mips/kernel/backtrace/thread-backtrace.c
--- linux-2.6.25.1/arch/mips/kernel/backtrace/thread-backtrace.c
1969-12-31 16:00:00.000000000 -0800
+++ linux-t/arch/mips/kernel/backtrace/thread-backtrace.c 2008-05-06
17:30:09.000000000 -0700
@@ -0,0 +1,388 @@
+/*
+ * thread-backtrace.c
+ *
+ * Performs Linux-dependent MIPS processor stack backtracing for
processes. It
+ * builds on the MIPS processor ABI-conformant stack backtracing code, but
+ * does the OS-dependent portions that handle signal frames, too. This
allows
+ * it to do multi-frame backtracing.
+ *
+ * Copyright(C) 2007 Scientific-Atlanta, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA
+ *
+ * Author: David VomLehn
+ */
+
+#ifdef __KERNEL__
+#include <linux/errno.h>
+#include <linux/module.h>
+#else
+#define FAKE_KERNEL
+#endif
+
+#ifdef FAKE_KERNEL
+#define __KERNEL__
+#endif
+#include <asm/sigframe.h>
+#ifdef FAKE_KERNEL
+#undef __KERNEL__
+#endif
+
+#include <asm/thread-backtrace.h>
+
+#ifndef numberof
+#define numberof(a) (sizeof(a) / sizeof ((a) [0]))
+#endif
+
+static int restore_sigcontext_regs(struct thread_bt *bt, reg_t ctx);
+static int set_sigargs(struct thread_bt *bt, reg_t sigframe_args,
+ reg_t sigargs [NUM_SIGARGS]);
+static int thread_backtrace_analyze_simple(struct thread_bt *bt);
+static int do_thread_backtrace (struct thread_bt *tbt,
+ process_thread_frame_t process, void *arg);
+
+void thread_backtrace_init(struct thread_bt *bt,
+ const struct thread_bt_config *config)
+{
+ bt->config = config;
+ simple_backtrace_init(&bt->sbt, &config->simple_config);
+ bt->pc_is_return_address = 0;
+}
+EXPORT_SYMBOL(thread_backtrace_init);
+
+/*
+ * Determine the program counter value of the caller.
+ * Returns: Return address, which is the location at which the call
+ * instruction is located.
+ */
+reg_t thread_backtrace_here(void)
+{
+ unsigned long ra;
+ __asm__ __volatile__(
+ " .set noat\n"
+ " la $at,%[ra]\n"
+ " sw $ra,0($at)\n"
+ : [ra] "=m" (ra)
+ :
+ : "at"
+ );
+ return ra;
+}
+EXPORT_SYMBOL(thread_backtrace_here);
+
+/*
+ * thread_backtrace_pt_regs - Backtrace the current thread.
+ */
+int thread_backtrace_pt_regs (const struct pt_regs *regs,
+ process_thread_frame_t process, void *arg,
+ const struct thread_bt_config *config)
+{
+ panic ("TODO: thread_backtrace_pt_regs not implemented\n");
+ return -ENOSYS;
+}
+
+/*
+ * Backtrace for a process the current function, including handling of
+ * signal frames.
+ * Params: process Function that will process each stack frame.
+ * arg Argument to passed to the processing function.
+ * Returns: Zero if successful, otherwise a negative number.
+ * value if not.
+ */
+int thread_backtrace(process_thread_frame_t process, void *arg,
+ const struct thread_bt_config *config)
+{
+ struct thread_bt bt;
+
+ thread_backtrace_init_here(&bt, config);
+ return do_thread_backtrace (&bt, process, arg);
+}
+
+/*
+ * do_thread_backtrace - Loop through the thread's stack
+ */
+static int do_thread_backtrace (struct thread_bt *bt,
+ process_thread_frame_t process, void *arg)
+{
+ int result;
+
+ for (result = thread_backtrace_first(bt);
+ result == 0;
+ result = thread_backtrace_next(bt)) {
+
+ /* If $pc is a return address, i.e. an address stored in $ra
+ * by a jalr instruction, adjust it to point to the jalr
+ * because, in some sense, that instruction is not yet
+ * complete. */
+ if (bt->pc_is_return_address) {
+ bt->sbt.pc -= 2 * OPCODE_SIZE;
+ bt_dbg(2, "$pc is return address, adjusted to 0x%lx\n",
+ bt->sbt.pc);
+ }
+
+ bt_dbg(1, "---\n");
+ result = process(arg, bt);
+
+ /* If we adjusted the pc to point to a jalr instruction,
+ * restore it */
+ if (bt->pc_is_return_address)
+ bt->sbt.pc += 2 * OPCODE_SIZE;
+
+ if (result != 0)
+ break;
+ }
+
+ /* If we got a return value of -ENOSYS, the $pc and $sp values are
+ * good, but we can't continue the backtrace. Call the function to
+ * process the last frame. */
+
+ if (result == -ENOSYS) {
+ (void) process(arg, bt);
+ result = 0;
+ }
+
+ /* The result could be zero because we internally determined that
+ * the stack backtrace is done, or because the process function
+ * passed back THREAD_BACKTRACE_END to indicate that the backtrace
+ * is done. Either case is normal, so adjust the result to indicate
+ * a normal termination. */
+ if (result > 0) {
+ bt_dbg(2, "Adjusting positive return code %d to zero", result);
+ result = 0;
+ }
+
+ return result;
+}
+EXPORT_SYMBOL(thread_backtrace);
+
+/*
+ * Get information for the first stack frame in a backtrace.
+ * Params: bt Pointer to trace_backtrace_t object initialized with
+ * thread_backtrace_init.
+ * Returns: Zero on success, a negative errno otherwise.
+ */
+int thread_backtrace_first(struct thread_bt *bt)
+{
+ int result;
+
+ result = thread_backtrace_analyze_frame(bt);
+
+ return result;
+}
+EXPORT_SYMBOL(thread_backtrace_first);
+
+/*
+ * Handle one stack backtrace frame, updating the register according to
what
+ * is found.
+ * Params: bt Pointer to register and backtrace information.
+ * Returns: Zero on success, a negative errno otherwise.
+ */
+int thread_backtrace_next(struct thread_bt *bt)
+{
+ int result;
+
+ result = thread_backtrace_pop_frame(bt);
+
+ if (result == 0)
+ result = thread_backtrace_analyze_frame(bt);
+
+ return result;
+}
+EXPORT_SYMBOL(thread_backtrace_next);
+
+/*
+ * Analyzes the current stack frame to see whether we are executing in a
+ * signal trampoline. To do this, we look at the instructions at the
+ * return address. If we have a load of $v0 with one of a few special
values,
+ * followed by a syscall, this frame is actually for a signal trampoline.
+ * If it is a signal trampoline, it will set the sigargs.
+ * Params: bt Pointer to backtrace register and other
+ * information.
+ * Returns: Zero if we were able to determine whether we were in a
+ * signal trampoline and a negative errno value if not.
+ */
+int thread_backtrace_analyze_frame(struct thread_bt *bt)
+{
+ int result;
+ opcode_t op1, op2;
+ reg_t ip;
+ reg_t sigframe_args;
+
+ bt_dbg(1, "$pc 0x%lx $sp 0x%lx\n", bt->sbt.pc,
+ bt->sbt.gprs [REG_SP]);
+ /* Start by trying to read two instructions since that's how long the
+ * signal trampoline is. */
+ ip = bt->sbt.pc;
+ result = bt->config->simple_config.get_op(ip, &op1);
+
+ if (result == 0)
+ result = bt->config->simple_config.get_op(ip_next(ip),
+ &op2);
+
+ if (result == 0) {
+ bt_dbg(3, "Possible signal trampoline ops: 0x%08x 0x%08x\n",
+ op1, op2);
+ /* If we could read the instructions, check to see whether
+ * they are the right ones for the signal trampoline. */
+ if (!is_li(op1, REG_V0) || !is_syscall(op2))
+ result = thread_backtrace_analyze_simple(bt);
+
+ else {
+ struct u_format *op_p;
+ /* Yes, we have an 'li v0,<value>' followed by a
+ * syscall. Is the system call we are doing the right
+ * kind? */
+ op_p = (struct u_format *) &op1;
+
+ switch (op_p->uimmediate) {
+ case __NR_O32_sigreturn: bt_dbg(2,
+ "Analyzing signal frame\n");
+ bt->type = THREAD_FRAME_SIGNAL;
+ bt->pc_is_return_address = 0;
+ sigframe_args = bt->sbt.gprs [REG_SP] +
+ offsetof(struct sigframe, sf_ass);
+ result = set_sigargs(bt, sigframe_args,
+ bt->sigargs);
+ break;
+ case __NR_O32_rt_sigreturn: bt_dbg(2,
+ "Analyzing realtime signal frame\n");
+ bt->type = THREAD_FRAME_RT_SIGNAL;
+ bt->pc_is_return_address = 0;
+ sigframe_args = bt->sbt.gprs [REG_SP] +
+ offsetof(struct rt_sigframe, rs_ass);
+ result = set_sigargs(bt, sigframe_args,
+ bt->sigargs);
+ break;
+ default: result = thread_backtrace_analyze_simple(bt);
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+EXPORT_SYMBOL(thread_backtrace_analyze_frame);
+
+/*
+ * This is not a signal frame, so use the analyzer for simple stack frames.
+ * Params: bt Pointer to struct thread_bt object
+ * Returns: Zero on success and a negative errno on failure.
+ */
+static int thread_backtrace_analyze_simple(struct thread_bt *bt)
+{
+ bt->type = THREAD_FRAME_SIMPLE;
+ bt->pc_is_return_address = 1;
+ return simple_backtrace_analyze_frame(&bt->sbt);
+}
+
+/*
+ * Advances to the next stack frame.
+ * Params: bt Pointer to a struct thread_bt object.
+ * Returns: Zero on success and a negative errno on failure.
+ */
+int thread_backtrace_pop_frame(struct thread_bt *bt)
+{
+ int result;
+ reg_t sc;
+
+ switch (bt->type) {
+ case THREAD_FRAME_SIMPLE: result =
+ simple_backtrace_pop_frame(&bt->sbt);
+ break;
+
+ case THREAD_FRAME_SIGNAL: sc = bt->sbt.gprs [REG_SP] +
+ offsetof(struct sigframe, sf_sc);
+ result = restore_sigcontext_regs(bt, sc);
+ break;
+
+ case THREAD_FRAME_RT_SIGNAL: sc = bt->sbt.gprs [REG_SP] +
+ offsetof(struct rt_sigframe, rs_uc.uc_mcontext);
+ result = restore_sigcontext_regs(bt, sc);
+ break;
+
+ default: result = -EINVAL; /* Internal failure: shouldn't happen */
+ bt_dbg(1, "Unexpected frame type: %d\n", bt->type);
+ break;
+ }
+
+ return result;
+}
+EXPORT_SYMBOL(thread_backtrace_pop_frame);
+
+/*
+ * Sets the backtrace information from the sigcontext object.
+ * Params: ctx Pointer to a sigcontext structure from which to take
+ * the register values.
+ * bt Pointer to the struct thread_bt object into which to
+ * place the register values.
+ * Returns: Zero if able to successfully read the information, a negative
+ * errno value otherwise.
+ */
+static int restore_sigcontext_regs(struct thread_bt *bt, reg_t ctx)
+{
+ int result = 0; /* Assume success */
+ reg_t sc_reg;
+ enum reg_num i;
+
+ bt_dbg(3, "sigcontext is at 0x%lx\n", ctx);
+
+ /* Restore all of the general purpose registers. */
+ simple_backtrace_clear_saved(&bt->sbt);
+
+ for (i = REG_AT; result == 0 && i < REG_ALL; i ++) {
+ sc_reg = ctx + offsetof(struct sigcontext, sc_regs [i]);
+ result = bt->config->get_sc_reg(sc_reg,
+ &bt->sbt.gprs [i]);
+ if (result == 0) {
+ bt_dbg(3, "Restored $r%d = 0x%lx\n", i,
+ bt->sbt.gprs [i]);
+ bt->sbt.gpr_saved [i] = 1;
+ }
+ }
+
+ /* We also have to get the $pc register because it is not a general
+ * purpose register. */
+ if (result == 0) {
+ result = bt->config->get_sc_reg(ctx +
+ offsetof(struct sigcontext, sc_pc),
+ &bt->sbt.pc);
+ if (result == 0)
+ bt_dbg(1, "Restored $pc = 0x%lx\n", bt->sbt.pc);
+ }
+
+ return result;
+}
+
+/*
+ * Copy the signal arguments into the struct thread_bt object.
+ * Params: args Pointer to the four argument values
+ * sigargs Pointer to array in which to store the
+ * argument values.
+ * Returns: Zero on success, a negative errno value on failure.
+ */
+static int set_sigargs(struct thread_bt *bt, reg_t sigframe_args,
+ reg_t sigargs [NUM_SIGARGS])
+{
+ int result = 0; /* Assume success */
+ unsigned i;
+
+ for (i = 0; result == 0 && i < NUM_SIGARGS; i ++) {
+ reg_t p;
+ p = sigframe_args + i * sizeof(reg_t);
+ result = bt->config->simple_config.get_reg(p, &sigargs [i]);
+ }
+
+ return result;
+}
diff -urN linux-2.6.25.1/arch/mips/kernel/entry.S
linux-t/arch/mips/kernel/entry.S
--- linux-2.6.25.1/arch/mips/kernel/entry.S 2008-05-01
14:45:25.000000000 -0700
+++ linux-t/arch/mips/kernel/entry.S 2008-05-06 17:30:09.000000000 -0700
@@ -40,6 +40,7 @@
andi t0, t0, KU_USER
beqz t0, resume_kernel
+ .globl resume_userspace
resume_userspace:
local_irq_disable # make sure we dont miss an
# interrupt setting need_resched
@@ -50,10 +51,12 @@
j restore_all
#ifdef CONFIG_PREEMPT
+ .globl resume_kernel
resume_kernel:
local_irq_disable
lw t0, TI_PRE_COUNT($28)
bnez t0, restore_all
+ .globl need_resched
need_resched:
LONG_L t0, TI_FLAGS($28)
andi t1, t0, _TIF_NEED_RESCHED
@@ -141,9 +144,11 @@
RESTORE_SP_AND_RET
.set at
+ .globl work_pending
work_pending:
andi t0, a2, _TIF_NEED_RESCHED # a2 is preloaded with TI_FLAGS
beqz t0, work_notifysig
+ .globl work_resched
work_resched:
jal schedule
@@ -157,6 +162,7 @@
andi t0, a2, _TIF_NEED_RESCHED
bnez t0, work_resched
+ .globl work_notifysig
work_notifysig: # deal with pending signals and
# notify-resume requests
move a0, sp
@@ -166,6 +172,7 @@
FEXPORT(syscall_exit_work_partial)
SAVE_STATIC
+ .global syscall_exit_work
syscall_exit_work:
li t0, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT
and t0, a2 # a2 is preloaded with TI_FLAGS
diff -urN linux-2.6.25.1/arch/mips/kernel/Makefile
linux-t/arch/mips/kernel/Makefile
--- linux-2.6.25.1/arch/mips/kernel/Makefile 2008-05-01
14:45:25.000000000 -0700
+++ linux-t/arch/mips/kernel/Makefile 2008-05-06 17:30:09.000000000 -0700
@@ -79,6 +79,7 @@
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+obj-$(CONFIG_MIPS_ADVANCED_BACKTRACE) += backtrace/
CFLAGS_cpu-bugs64.o = $(shell if $(CC) $(KBUILD_CFLAGS) -Wa,-mdaddi -c
-o /dev/null -xc /dev/null >/dev/null 2>&1; then echo
"-DHAVE_AS_SET_DADDI"; fi)
diff -urN linux-2.6.25.1/arch/mips/kernel/scall32-o32.S
linux-t/arch/mips/kernel/scall32-o32.S
--- linux-2.6.25.1/arch/mips/kernel/scall32-o32.S 2008-05-01
14:45:25.000000000 -0700
+++ linux-t/arch/mips/kernel/scall32-o32.S 2008-05-06 17:30:09.000000000
-0700
@@ -54,6 +54,7 @@
sw a3, PT_R26(sp) # save a3 for syscall restarting
bgez t3, stackargs
+ .globl stack_done
stack_done:
lw t0, TI_FLAGS($28) # syscall tracing enabled?
li t1, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT
@@ -72,6 +73,7 @@
# restarting
1: sw v0, PT_R2(sp) # result
+ .globl o32_syscall_exit
o32_syscall_exit:
local_irq_disable # make sure need_resched and
# signals dont change between
@@ -83,11 +85,13 @@
j restore_partial
+ .globl o32_syscall_exit_work
o32_syscall_exit_work:
j syscall_exit_work_partial
/*
------------------------------------------------------------------------ */
+ .globl syscall_trace_entry
syscall_trace_entry:
SAVE_STATIC
move s0, t2
@@ -122,6 +126,7 @@
* stack arguments from the user stack to the kernel stack.
* This Sucks (TM).
*/
+ .globl stackargs
stackargs:
lw t0, PT_R29(sp) # get old user stack pointer
@@ -132,7 +137,7 @@
lw t5, TI_ADDR_LIMIT($28)
addu t4, t0, 32
and t5, t4
- bltz t5, bad_stack # -> sp is bad
+ bltz t5, _bad_stack # -> sp is bad
/* Ok, copy the args from the luser stack to the kernel stack.
* t3 is the precomputed number of instruction bytes needed to
@@ -162,17 +167,18 @@
.set pop
.section __ex_table,"a"
- PTR 1b,bad_stack
- PTR 2b,bad_stack
- PTR 3b,bad_stack
- PTR 4b,bad_stack
+ PTR 1b,_bad_stack
+ PTR 2b,_bad_stack
+ PTR 3b,_bad_stack
+ PTR 4b,_bad_stack
.previous
/*
* The stackpointer for a call with more than 4 arguments is bad.
* We probably should handle this case a bit more drastic.
*/
-bad_stack:
+ .globl _bad_stack
+_bad_stack:
negu v0 # error
sw v0, PT_R0(sp)
sw v0, PT_R2(sp)
@@ -183,6 +189,7 @@
/*
* The system call does not exist in this kernel
*/
+ .globl illegal_syscall
illegal_syscall:
li v0, -ENOSYS # error
sw v0, PT_R2(sp)
@@ -213,8 +220,8 @@
#endif
.section __ex_table,"a"
- PTR 1b, bad_stack
- PTR 2b, bad_stack
+ PTR 1b, _bad_stack
+ PTR 2b, _bad_stack
.previous
#else
sw a1, 16(sp)
@@ -246,13 +253,16 @@
j o32_syscall_exit # continue like a normal syscall
+ .globl no_mem
no_mem: li v0, -ENOMEM
jr ra
+ .globl bad_address
bad_address:
li v0, -EFAULT
jr ra
+ .globl bad_alignment
bad_alignment:
li v0, -EINVAL
jr ra
@@ -305,6 +315,7 @@
jr t2
/* Unreached */
+ .globl einval
einval: li v0, -EINVAL
jr ra
END(sys_syscall)
diff -urN linux-2.6.25.1/arch/mips/kernel/signal32.c
linux-t/arch/mips/kernel/signal32.c
--- linux-2.6.25.1/arch/mips/kernel/signal32.c 2008-05-01
14:45:25.000000000 -0700
+++ linux-t/arch/mips/kernel/signal32.c 2008-05-06 17:30:09.000000000 -0700
@@ -32,16 +32,10 @@
#include <asm/system.h>
#include <asm/fpu.h>
#include <asm/war.h>
+#include <asm/sigframe.h>
#include "signal-common.h"
-/*
- * Including <asm/unistd.h> would give use the 64-bit syscall numbers ...
- */
-#define __NR_O32_sigreturn 4119
-#define __NR_O32_rt_sigreturn 4193
-#define __NR_O32_restart_syscall 4253
-
/* 32-bit compatibility types */
typedef unsigned int __sighandler32_t;
diff -urN linux-2.6.25.1/arch/mips/kernel/signal.c
linux-t/arch/mips/kernel/signal.c
--- linux-2.6.25.1/arch/mips/kernel/signal.c 2008-05-01
14:45:25.000000000 -0700
+++ linux-t/arch/mips/kernel/signal.c 2008-05-06 17:30:09.000000000 -0700
@@ -30,51 +30,11 @@
#include <asm/ucontext.h>
#include <asm/cpu-features.h>
#include <asm/war.h>
+#include <asm/sigframe.h>
#include "signal-common.h"
/*
- * Horribly complicated - with the bloody RM9000 workarounds enabled
- * the signal trampolines is moving to the end of the structure so we can
- * increase the alignment without breaking software compatibility.
- */
-#if ICACHE_REFILLS_WORKAROUND_WAR == 0
-
-struct sigframe {
- u32 sf_ass[4]; /* argument save space for o32 */
- u32 sf_code[2]; /* signal trampoline */
- struct sigcontext sf_sc;
- sigset_t sf_mask;
-};
-
-struct rt_sigframe {
- u32 rs_ass[4]; /* argument save space for o32 */
- u32 rs_code[2]; /* signal trampoline */
- struct siginfo rs_info;
- struct ucontext rs_uc;
-};
-
-#else
-
-struct sigframe {
- u32 sf_ass[4]; /* argument save space for o32 */
- u32 sf_pad[2];
- struct sigcontext sf_sc; /* hw context */
- sigset_t sf_mask;
- u32 sf_code[8] ____cacheline_aligned; /* signal trampoline */
-};
-
-struct rt_sigframe {
- u32 rs_ass[4]; /* argument save space for o32 */
- u32 rs_pad[2];
- struct siginfo rs_info;
- struct ucontext rs_uc;
- u32 rs_code[8] ____cacheline_aligned; /* signal trampoline */
-};
-
-#endif
-
-/*
* Helper routines
*/
static int protected_save_fp_context(struct sigcontext __user *sc)
diff -urN linux-2.6.25.1/arch/mips/kernel/traps.c
linux-t/arch/mips/kernel/traps.c
--- linux-2.6.25.1/arch/mips/kernel/traps.c 2008-05-01
14:45:25.000000000 -0700
+++ linux-t/arch/mips/kernel/traps.c 2008-05-06 17:33:13.000000000 -0700
@@ -42,6 +42,7 @@
#include <asm/mmu_context.h>
#include <asm/types.h>
#include <asm/stacktrace.h>
+#include <asm/kernel-backtrace.h>
extern asmlinkage void handle_int(void);
extern asmlinkage void handle_tlbm(void);
@@ -105,21 +106,83 @@
__setup("raw_show_trace", set_raw_show_trace);
#endif
-static void show_backtrace(struct task_struct *task, const struct
pt_regs *regs)
+#ifdef CONFIG_MIPS_ADVANCED_BACKTRACE
+/*
+ * print_bt_frame - Print one backtrace frame
+ * @arg: Pointer to the frame count
+ * @bt: Pointer to the kernel backtrace structure &kernel_bt.
+ * Returns: Zero if the backtrace is to continue, otherwise
+ * KERNEL_BACKTRACE_END to indicate backtrace termination.
+ */
+static int print_bt_frame(void *arg, struct kernel_bt *bt)
+{
+ int result = 0; /* Assume a good outcome */
+ unsigned *count = (unsigned *) arg;
+ unsigned long pc, fp;
+ static const int max_stack_frames = 50;
+
+ pc = bt->tbt.sbt.pc;
+ fp = bt->tbt.sbt.gprs [bt->tbt.sbt.framepointer];
+ print_ip_sym(pc);
+
+ /* Don't print more than the maximum number of stack frames. */
+ *count = *count + 1;
+
+ if (*count >= max_stack_frames) {
+ printk(KERN_WARNING "Exceeded maximum number of frames (%u)\n",
+ max_stack_frames);
+ result = KERNEL_BACKTRACE_END;
+ }
+
+ return result;
+}
+
+/**
+ * show_unwinding_backtrace - Print the backtrace by unwinding stack frames
+ * one at a time.
+ * @task: Pointer to the task structure for the task to be unwound
+ * @regs: Pointer to &pt_regs structure with the initial registers
+ */
+static void show_unwinding_backtrace(struct task_struct *task,
+ const struct pt_regs *regs)
+{
+ int frame_count = 0;
+ int rc;
+
+ rc = kernel_backtrace_pt_regs(regs, print_bt_frame, &frame_count);
+
+ if (rc != 0)
+ printk(KERN_WARNING "Backtrace terminated with error %d\n",
+ rc);
+}
+#else
+static void show_unwinding_backtrace(struct task_struct *task,
+ const struct pt_regs *regs)
{
unsigned long sp = regs->regs[29];
unsigned long ra = regs->regs[31];
unsigned long pc = regs->cp0_epc;
- if (raw_show_trace || !__kernel_text_address(pc)) {
- show_raw_backtrace(sp);
- return;
- }
- printk("Call Trace:\n");
do {
print_ip_sym(pc);
pc = unwind_stack(task, &sp, pc, &ra);
} while (pc);
+}
+#endif
+
+static void show_backtrace(struct task_struct *task, const struct
pt_regs *regs)
+{
+ unsigned long sp = regs->regs[29];
+ unsigned long pc = regs->cp0_epc;
+
+ if (raw_show_trace
+ || !__kernel_text_address(pc)
+ ) {
+ show_raw_backtrace(sp);
+ return;
+ }
+ printk("Symbolic Call Trace:\n");
+ show_unwinding_backtrace(task, regs);
printk("\n");
}
diff -urN linux-2.6.25.1/include/asm-mips/kernel-backtrace.h
linux-t/include/asm-mips/kernel-backtrace.h
--- linux-2.6.25.1/include/asm-mips/kernel-backtrace.h 1969-12-31
16:00:00.000000000 -0800
+++ linux-t/include/asm-mips/kernel-backtrace.h 2008-05-06
17:30:09.000000000 -0700
@@ -0,0 +1,81 @@
+/*
+ * kernel-backtrace.h
+ *
+ * Definitions for stack backtracing in the kernel. In addition to handle
+ * process backtraces, because kernel threads can get signals, too,
this has
+ * to handle interrupts and exceptions.
+ *
+ * Copyright (C) 2007 Scientific-Atlanta, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+#ifndef _KERNEL_BACKTRACE_H_
+#define _KERNEL_BACKTRACE_H_
+#include <asm/types.h>
+#include <asm/thread-backtrace.h>
+
+/* Value to be returned by the function that processes each frame to
indicate
+ * a non-error termination of the backtrace. */
+#define KERNEL_BACKTRACE_END 1
+
+/* Value returned internally by functions called from
do_kernel_backtrace to
+ * indicate that the backtrace should terminate. */
+#define KERNEL_BACKTRACE_DONE 2
+
+enum kernel_frame_type {
+ KERNEL_FRAME_SIMPLE = THREAD_FRAME_SIMPLE, /* Ordinary frame */
+ KERNEL_FRAME_SIGNAL = THREAD_FRAME_SIGNAL, /* Signal frame */
+ KERNEL_FRAME_RT_SIGNAL = THREAD_FRAME_RT_SIGNAL, /* Realtime signal */
+ KERNEL_FRAME_TLB_REFILL, /* In the general exception vector */
+ KERNEL_FRAME_GENERAL_EXCEPTION, /* In the general exception vector */
+ KERNEL_FRAME_INTERRUPT, /* In the general exception vector */
+ KERNEL_FRAME_K0_K1_ONLY, /* Code uses only $k0 & $k1 registers */
+ KERNEL_FRAME_SAVE_SOME, /* Code uses SAVE_SOME macro */
+ KERNEL_FRAME_SAVE_STATIC, /* Code uses SAVE_STATIC macro */
+ KERNEL_FRAME_SAVE_ALL, /* Code uses SAVE_ALL macro */
+ KERNEL_FRAME_GLUE, /* Registers saved in struct pt_regs */
+ KERNEL_FRAME_RESTORE_SOME, /* Code uses RESTORE_SOME macro */
+ KERNEL_FRAME_UNEXPECTED /* Never expect to be in this code */
+};
+
+struct kernel_bt {
+ struct thread_bt tbt;
+ enum kernel_frame_type type;
+ u32 cp0_status; /* CP0 Status register */
+ reg_t cp0_epc; /* CP0 EPC register */
+ reg_t start; /* Start of current section of code */
+ reg_t size; /* Size of current section of code */
+};
+
+/**
+ * process_kernel_frame_t - Type of function called to do processing
for each
+ * frame of the stack backtrace.
+ * @arg: Argument passed to kernel_backtrace/kernel_backtrace_pt_regs
+ * @bt: Pointer to &kernel_bt structure
+ * Returns zero if the backtrace is to continue, non-zero otherwise. A
negative
+ * errno is used to signal an error, KERNEL_BACKTRACE_END is available for
+ * a normal termination.
+ */
+typedef int (*process_kernel_frame_t) (void *arg, struct kernel_bt *bt);
+
+extern int kernel_backtrace_pt_regs(const struct pt_regs *regs,
+ process_kernel_frame_t process, void *arg);
+extern int kernel_backtrace(process_kernel_frame_t process, void *arg);
+extern int kernel_backtrace_first(struct kernel_bt *bt);
+extern int kernel_backtrace_next(struct kernel_bt *bt);
+extern int kernel_backtrace_analyze_frame(struct kernel_bt *bt);
+extern int kernel_backtrace_pop_frame(struct kernel_bt *bt);
+#endif /* _KERNEL_BACKTRACE_H_ */
diff -urN linux-2.6.25.1/include/asm-mips/kernel-backtrace-symbols.h
linux-t/include/asm-mips/kernel-backtrace-symbols.h
--- linux-2.6.25.1/include/asm-mips/kernel-backtrace-symbols.h
1969-12-31 16:00:00.000000000 -0800
+++ linux-t/include/asm-mips/kernel-backtrace-symbols.h 2008-05-06
17:30:09.000000000 -0700
@@ -0,0 +1,133 @@
+/*
+ * kernel-backtrace-symbols.h
+ *
+ * This file contains symbols that need to be handled specially. The
+ * idea is that code will define a macro named SPECIAL_SYMBOL that
+ * appropriately generates code for an external reference, then redefines
+ * it to generate a table with the symbols.
+ *
+ * It should be mentioned that, though this is not especially pretty,
having
+ * this list of symbols in only one place makes it impossible to define
+ * the external reference for the symbol and then forget to add it to the
+ * table of symbols, which is a big win on the maintenance front.
+ *
+ * To make this list easier to maintain, it is best to keep the symbols
+ * for a given file together and in the order in which they appear in that
+ * file.
+ *
+ * Copyright (C) 2007 Scientific-Atlanta, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+/* The first part is protected against multiple inclusions, but the
+ * rest isn't */
+#ifndef _KERNEL_BACKTRACE_SYMBOLS_H_
+#define _KERNEL_BACKTRACE_SYMBOLS_H_
+#include <asm/kernel-backtrace.h>
+
+struct kern_special_sym {
+ const opcode_t *start;
+ enum kernel_frame_type type;
+};
+
+extern const struct kern_special_sym kernel_backtrace_symbols [];
+extern unsigned kernel_backtrace_symbols_size;
+#endif /* _KERNEL_BACKTRACE_SYMBOLS_H_ */
+
+/* Only define the symbol list if someone has defined the macro we use
+ * to construct it. This allows the above symbols to be included even by
+ * things which only reference the list. */
+
+#ifdef SPECIAL_SYMBOL
+/* arch/mips/kernel/entry.S */
+SPECIAL_SYMBOL(ret_from_exception, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(ret_from_irq, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(resume_userspace, KERNEL_FRAME_GLUE)
+#ifdef CONFIG_PREEMPT
+SPECIAL_SYMBOL(resume_kernel, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(need_resched, KERNEL_FRAME_GLUE)
+#endif
+SPECIAL_SYMBOL(ret_from_fork, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(syscall_exit, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(restore_all, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(restore_partial, KERNEL_FRAME_RESTORE_SOME)
+SPECIAL_SYMBOL(work_pending, KERNEL_FRAME_RESTORE_SOME)
+SPECIAL_SYMBOL(work_resched, KERNEL_FRAME_RESTORE_SOME)
+SPECIAL_SYMBOL(work_notifysig, KERNEL_FRAME_RESTORE_SOME)
+SPECIAL_SYMBOL(syscall_exit_work_partial, KERNEL_FRAME_SAVE_STATIC)
+SPECIAL_SYMBOL(syscall_exit_work, KERNEL_FRAME_GLUE)
+
+/* arch/mips/kernel/genex.S */
+SPECIAL_SYMBOL(except_vec_vi_end, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(except_vec_vi_handler, KERNEL_FRAME_SAVE_STATIC)
+SPECIAL_SYMBOL(handle_adel, KERNEL_FRAME_SAVE_ALL)
+SPECIAL_SYMBOL(handle_adel_int, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(handle_ades, KERNEL_FRAME_SAVE_ALL)
+SPECIAL_SYMBOL(handle_ades_int, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(handle_ibe, KERNEL_FRAME_SAVE_ALL)
+SPECIAL_SYMBOL(handle_ibe_int, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(handle_dbe, KERNEL_FRAME_SAVE_ALL)
+SPECIAL_SYMBOL(handle_dbe_int, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(handle_bp, KERNEL_FRAME_SAVE_ALL)
+SPECIAL_SYMBOL(handle_bp_int, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(handle_ri, KERNEL_FRAME_SAVE_ALL)
+SPECIAL_SYMBOL(handle_ri_int, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(handle_cpu, KERNEL_FRAME_SAVE_ALL)
+SPECIAL_SYMBOL(handle_cpu_int, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(handle_ov, KERNEL_FRAME_SAVE_ALL)
+SPECIAL_SYMBOL(handle_ov_int, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(handle_tr, KERNEL_FRAME_SAVE_ALL)
+SPECIAL_SYMBOL(handle_tr_int, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(handle_fpe, KERNEL_FRAME_SAVE_ALL)
+SPECIAL_SYMBOL(handle_fpe_int, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(handle_mdmx, KERNEL_FRAME_SAVE_ALL)
+SPECIAL_SYMBOL(handle_mdmx_int, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(handle_watch, KERNEL_FRAME_SAVE_ALL)
+SPECIAL_SYMBOL(handle_watch_int, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(handle_mcheck, KERNEL_FRAME_SAVE_ALL)
+SPECIAL_SYMBOL(handle_mcheck_int, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(handle_mt, KERNEL_FRAME_SAVE_ALL)
+SPECIAL_SYMBOL(handle_mt_int, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(handle_dsp, KERNEL_FRAME_SAVE_ALL)
+SPECIAL_SYMBOL(handle_dsp_int, KERNEL_FRAME_GLUE)
+
+/* arch/mips/kernel/scall32-o32.S */
+SPECIAL_SYMBOL(handle_sys, KERNEL_FRAME_SAVE_SOME)
+SPECIAL_SYMBOL(stack_done, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(o32_syscall_exit, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(o32_syscall_exit_work, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(syscall_trace_entry, KERNEL_FRAME_SAVE_STATIC)
+SPECIAL_SYMBOL(stackargs, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(_bad_stack, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(illegal_syscall, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(mips_atomic_set, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(mips_atomic_set, KERNEL_FRAME_SAVE_STATIC)
+SPECIAL_SYMBOL(no_mem, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(bad_address, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(bad_alignment, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(sys_sysmips, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(sys_syscall, KERNEL_FRAME_GLUE)
+SPECIAL_SYMBOL(einval, KERNEL_FRAME_GLUE)
+
+/* arch/mips/mm/tlbex-fault.S */
+SPECIAL_SYMBOL(tlb_do_page_fault_0, KERNEL_FRAME_SAVE_ALL)
+SPECIAL_SYMBOL(tlb_do_page_fault_1, KERNEL_FRAME_SAVE_ALL)
+
+/* arch/mips/mm/tlbex.c (Code generated dynamically) */
+SPECIAL_SYMBOL(handle_tlbl, KERNEL_FRAME_K0_K1_ONLY)
+SPECIAL_SYMBOL(handle_tlbm, KERNEL_FRAME_K0_K1_ONLY)
+SPECIAL_SYMBOL(handle_tlbs, KERNEL_FRAME_K0_K1_ONLY)
+#endif /* SPECIAL_SYMBOL */
diff -urN linux-2.6.25.1/include/asm-mips/ptrace.h
linux-t/include/asm-mips/ptrace.h
--- linux-2.6.25.1/include/asm-mips/ptrace.h 2008-05-01
14:45:25.000000000 -0700
+++ linux-t/include/asm-mips/ptrace.h 2008-05-06 18:27:15.000000000 -0700
@@ -23,6 +23,18 @@
#define DSP_CONTROL 77
#define ACX 78
+/* Register numbers */
+enum reg_num {
+ REG_ZERO, REG_AT, REG_V0, REG_V1,
+ REG_A0, REG_A1, REG_A2, REG_A3,
+ REG_T0, REG_T1, REG_T2, REG_T3,
+ REG_T4, REG_T5, REG_T6, REG_T7,
+ REG_S0, REG_S1, REG_S2, REG_S3,
+ REG_S4, REG_S5, REG_S6, REG_S7,
+ REG_T8, REG_T9, REG_K0, REG_K1,
+ REG_GP, REG_SP, REG_S8, REG_RA,
+ REG_ALL /* This last one is the number of GPRs */
+};
/*
* This struct defines the way the registers are stored on the stack
during a
* system call/exception. As usual the registers k0/k1 aren't being saved.
@@ -34,7 +46,7 @@
#endif
/* Saved main processor registers. */
- unsigned long regs[32];
+ unsigned long regs[REG_ALL];
/* Saved special registers. */
unsigned long cp0_status;
diff -urN linux-2.6.25.1/include/asm-mips/sigframe.h
linux-t/include/asm-mips/sigframe.h
--- linux-2.6.25.1/include/asm-mips/sigframe.h 1969-12-31
16:00:00.000000000 -0800
+++ linux-t/include/asm-mips/sigframe.h 2008-05-06 17:30:09.000000000 -0700
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ *
+ * Copyright (c) 2007 Scientific-Atlanta, Inc.
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1994 - 2000 Ralf Baechle
+ * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
+ */
+
+#ifndef _SIGFRAME_H_
+#define _SIGFRAME_H_
+#include <linux/signal.h>
+
+#include <asm/ucontext.h>
+#include <asm/war.h>
+
+/*
+ * Originally from arch/mips/kernel/signal.c...
+ */
+/*
+ * Horribly complicated - with the bloody RM9000 workarounds enabled
+ * the signal trampolines is moving to the end of the structure so we can
+ * increase the alignment without breaking software compatibility.
+ */
+#if ICACHE_REFILLS_WORKAROUND_WAR == 0
+
+struct sigframe {
+ u32 sf_ass[4]; /* argument save space for o32 */
+ u32 sf_code[2]; /* signal trampoline */
+ struct sigcontext sf_sc;
+ sigset_t sf_mask;
+};
+
+struct rt_sigframe {
+ u32 rs_ass[4]; /* argument save space for o32 */
+ u32 rs_code[2]; /* signal trampoline */
+ struct siginfo rs_info;
+ struct ucontext rs_uc;
+};
+
+#else
+
+struct sigframe {
+ u32 sf_ass[4]; /* argument save space for o32 */
+ u32 sf_pad[2];
+ struct sigcontext sf_sc; /* hw context */
+ sigset_t sf_mask;
+ u32 sf_code[8] ____cacheline_aligned; /* signal trampoline */
+};
+
+struct rt_sigframe {
+ u32 rs_ass[4]; /* argument save space for o32 */
+ u32 rs_pad[2];
+ struct siginfo rs_info;
+ struct ucontext rs_uc;
+ u32 rs_code[8] ____cacheline_aligned; /* signal trampoline */
+};
+
+#endif
+
+/*
+ * Originally from arch/mips/kernel/signal32.c...
+ */
+
+/*
+ * Including <asm/unistd.h> would give use the 64-bit syscall numbers ...
+ */
+#define __NR_O32_sigreturn 4119
+#define __NR_O32_rt_sigreturn 4193
+#define __NR_O32_restart_syscall 4253
+#endif
diff -urN linux-2.6.25.1/include/asm-mips/simple-backtrace.h
linux-t/include/asm-mips/simple-backtrace.h
--- linux-2.6.25.1/include/asm-mips/simple-backtrace.h 1969-12-31
16:00:00.000000000 -0800
+++ linux-t/include/asm-mips/simple-backtrace.h 2008-05-06
17:30:09.000000000 -0700
@@ -0,0 +1,344 @@
+/*
+ * simple-backtrace.h
+ *
+ * Definitions handling one simple stack frame's worth of stack backtrace.
+ *
+ * Copyright(C) 2007 Scientific-Atlanta, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+#ifndef _SIMPLE_BACKTRACE_H_
+#define _SIMPLE_BACKTRACE_H_
+#include <linux/compiler.h>
+#include <asm/inst.h>
+#include <asm/ptrace.h>
+
+#ifdef __KERNEL__
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#define dbg_print printk
+#define PRIORITY KERN_CRIT
+#else
+#include <errno.h>
+#include <stdio.h>
+#define dbg_print printf
+#define EXPORT_SYMBOL(sym)
+#define PRIORITY
+#endif
+
+#define BACKTRACE_DEBUG 0
+
+#if BACKTRACE_DEBUG != 0
+#define bt_dbg(level, fmt, ...) do { \
+ if (level <= BACKTRACE_DEBUG) \
+ dbg_print(PRIORITY "%s: " fmt, __FUNCTION__, \
+ ## __VA_ARGS__); \
+ } while (0)
+#else
+#define bt_dbg(level, fmt, ...) do { } while (0)
+#endif
+
+#define OPCODE_SIZE (sizeof(opcode_t)) /* MIPS processor has 4-byte
opcodes */
+
+/* Additional instruction formats that really should have been defined in
+ * asm/inst.h */
+#ifdef __MIPSEB__
+ struct any_format { /* Generic format */
+ unsigned int opcode : 6;
+ unsigned int remainder : 26;
+ };
+
+ struct eret_format {
+ unsigned int opcode : 6;
+ unsigned int co : 1;
+ unsigned int zero: 19;
+ unsigned int func : 6;
+ };
+
+ struct syscall_format {
+ unsigned int opcode : 6;
+ unsigned int code : 20;
+ unsigned int func : 6;
+ };
+#else
+ struct any_format { /* Generic format */
+ unsigned int remainder : 26;
+ unsigned int opcode : 6;
+ };
+
+ struct eret_format {
+ unsigned int func : 6;
+ unsigned int zero: 19;
+ unsigned int co : 1;
+ unsigned int opcode : 6;
+ };
+
+ struct syscall_format {
+ unsigned int func : 6;
+ unsigned int code : 20;
+ unsigned int opcode : 6;
+ };
+#endif
+
+/* NULL value, converted to a reg_t type */
+#define NULL_REG ((reg_t) NULL)
+
+typedef unsigned long reg_t; /* Can hold a register value */
+typedef mips_instruction opcode_t; /* Holds a machine instruction */
+typedef int reg_offset_t; /* Offset from a register */
+typedef reg_offset_t frame_size_t; /* Stack frame size */
+typedef long pc_offset_t; /* Offset from $pc */
+
+/* Backtrace types */
+enum simple_bt_type {
+ SIMPLE_BACKTRACE_O32_ABI, /* Backtrace using o32 ABI rules */
+ SIMPLE_BACKTRACE_LOOKUP_FUNC, /* Look up function start and size */
+};
+
+/*
+ * Functions that are required by the simple-backtrace.c code but which
must
+ * be supplied by users of that code.
+ */
+
+/*
+ * Get an opcode_t-sized object from memory.
+ * Params: ip Address of the opcode.
+ * op Location in which to store the opcode
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+typedef int(*get_op_t) (reg_t ip, opcode_t *op);
+
+/*
+ * Get a register-sized(reg_t-sized) object from memory.
+ * Params: rp Address of the value.
+ * reg Location in which to store the value.
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+typedef int(*get_reg_t) (reg_t rp, reg_t *reg);
+
+/*
+ * Lookup a symbol's starting address and the size of the object, given an
+ * address within the symbol.
+ * Params: addr Address to look up
+ * symbolstart Starting address of the symbol
+ * symbolsize Number of bytes in the memory represented by
+ * the symbol.
+ * Returns: Zero on success, otherwise a negative errno value. If the
symbol
+ * couldn't be found, the value returned should be -ESRCH.
+ */
+typedef int(*symbol_lookup_t) (reg_t addr, reg_t *symbolstart,
+ reg_t *symbolsize);
+
+/*
+ * Configuration information for the backtrace.
+ */
+struct simple_bt_config {
+ enum simple_bt_type type;
+ get_op_t get_op;
+ get_reg_t get_reg;
+ symbol_lookup_t symbol_lookup;
+};
+
+/*
+ * Data type that holds the current values of the GPRS, along with other
+ * information required during the backtrace.
+ */
+struct simple_bt {
+ /* Public */
+ reg_t gprs [REG_ALL];
+ int gpr_saved [REG_ALL];
+ enum reg_num framepointer; /* Register number of frame pointer */
+ reg_t pc;
+ frame_size_t frame_size; /* Stack frame size, in bytes */
+ /* Private */
+ const struct simple_bt_config *config;
+ reg_t sf_allocation; /* Address of stack frame allocation */
+ reg_t fp_restore; /* Addr frame ptr moved back to $sp*/
+ reg_t sf_deallocation; /* Addr of stack frame deallocation */
+ reg_t fn_return; /* Address of return instruction */
+ enum reg_num possible_framepointer;
+ reg_t func_start; /* Address of 1st opcode of function */
+ union {
+ struct {
+ reg_t func_size;
+ } lookup;
+ } data;
+};
+
+/* Opcode: addiu rs, rt, imm */
+static inline int is_addiu(opcode_t inst, reg_t rs, reg_t rt)
+{
+ struct i_format *op_p = (struct i_format *) &inst;
+ return op_p->opcode == addiu_op &&
+ op_p->rs == rs && op_p->rt == rt;
+}
+
+/* Opcode: addu */
+static inline int is_addu_noreg(opcode_t inst)
+{
+ struct r_format *op_p = (struct r_format *) &inst;
+ return op_p->opcode == spec_op && op_p->func == addu_op &&
+ op_p->re == 0;
+}
+
+/* Opcode: addu rd, rs, rt */
+static inline int is_addu(opcode_t inst, reg_t rd, reg_t rs, reg_t rt)
+{
+ struct r_format *op_p = (struct r_format *) &inst;
+ return is_addu_noreg(inst) &&
+ op_p->rd == rd && op_p->rs == rs && op_p->rt == rt;
+}
+
+/* Opcode: eret */
+static inline int is_eret(opcode_t inst)
+{
+ struct eret_format *op_p = (struct eret_format *) &inst;
+ return op_p->opcode == spec_op && op_p->func == eret_op &&
+ op_p->co == 1 && op_p->zero == 0;
+}
+
+/* Opcode: move rd, rs. Synonym for addu rd, rs, zero. */
+static inline int is_move(opcode_t inst, reg_t rd, reg_t rs)
+{
+ return is_addu(inst, rd, rs, REG_ZERO);
+}
+
+/* Opcode: jal addr */
+static inline int is_jal(opcode_t inst)
+{
+ struct j_format *op_p = (struct j_format *) &inst;
+ return op_p->opcode == jal_op;
+}
+
+static inline int is_jalr(opcode_t inst)
+{
+ struct r_format *op_p = (struct r_format *) &inst;
+ return op_p->opcode == spec_op && op_p->func == jalr_op &&
+ op_p->rt == 0;
+}
+
+static inline int is_jr(opcode_t inst, reg_t rs)
+{
+ struct r_format *op_p = (struct r_format *) &inst;
+ return op_p->opcode == spec_op && op_p->func == jr_op &&
+ op_p->rs == rs && op_p->rt == 0 && op_p->rd == 0;
+}
+
+static inline int is_li(opcode_t inst, reg_t rt)
+{
+ return is_addiu(inst, REG_ZERO, rt);
+}
+
+static inline int is_lui(opcode_t inst, reg_t rt)
+{
+ struct u_format *op_p = (struct u_format *) &inst;
+ return op_p->opcode == lui_op && op_p->rs == 0 &&
+ op_p->rt == rt;
+}
+
+static inline int is_sw(opcode_t inst, reg_t rt, reg_t base)
+{
+ struct i_format *op_p = (struct i_format *) &inst;
+ return op_p->opcode == sw_op &&
+ op_p->rs == base && op_p->rt == rt;
+}
+
+static inline int is_syscall(opcode_t inst)
+{
+ struct syscall_format *op_p = (struct syscall_format *) &inst;
+ return op_p->opcode == spec_op &&
+ op_p->func == syscall_op;
+}
+
+/* Sign extension functions. */
+
+static inline reg_offset_t sign_extend_imm(opcode_t imm)
+{
+ struct i_format *op_p = (struct i_format *) &imm;
+ return op_p->simmediate;
+}
+
+static inline reg_offset_t sign_extend_offset(opcode_t offset)
+{
+ struct i_format *op_p = (struct i_format *) &offset;
+ return op_p->simmediate;
+}
+
+/* Is this the save of a register with an offset from the given frame
pointer
+ * register? */
+static inline int is_frame_save(opcode_t inst, enum reg_num framepointer)
+{
+ struct i_format *op_p = (struct i_format *) &inst;
+ return op_p->opcode == sw_op &&
+ op_p->rs == framepointer;
+}
+
+/* Returns the number of the register being saved when the opcode is a
frame
+ * save. */
+static inline enum reg_num frame_save_rt(opcode_t op)
+{
+ struct i_format *op_p = (struct i_format *) &op;
+ return op_p->rt;
+}
+
+/* Returns the offset in the stack frame for a frame save */
+static inline reg_offset_t frame_save_offset(opcode_t op)
+{
+ struct i_format *op_p = (struct i_format *) &op;
+ return op_p->simmediate;
+}
+
+/*
+ * Add a number of bytes to the given instruction pointer
+ * Params: ip Instruction pointer
+ * delta Number of bytes to add
+ * Returns: The sum of ip and delta, as an opcode_t *.
+ */
+static inline reg_t ip_add(reg_t ip, pc_offset_t delta)
+{
+ return ip + delta;
+}
+
+/*
+ * Compute the location of the next instruction.
+ * Params: ip Pointer to the current instruction
+ * Returns: Pointer to the next instruction.
+ */
+static inline reg_t ip_next(reg_t ip)
+{
+ return ip_add(ip, OPCODE_SIZE);
+}
+
+/*
+ * Compute the location of the previous instruction.
+ * Params: ip Pointer to the current instruction
+ * Returns: Pointer to the previous instruction.
+ */
+static inline reg_t ip_prev(reg_t ip)
+{
+ return ip_add(ip, -OPCODE_SIZE);
+}
+
+extern int is_basic_block_end(opcode_t op);
+extern void simple_backtrace_clear_saved(struct simple_bt *bt);
+extern void simple_backtrace_init(struct simple_bt *bt,
+ const struct simple_bt_config *config);
+extern int simple_backtrace_first(struct simple_bt *bt);
+extern int simple_backtrace_next(struct simple_bt *bt);
+extern int simple_backtrace_analyze_frame(struct simple_bt *bt);
+extern int simple_backtrace_pop_frame(struct simple_bt *bt);
+#endif /* _SIMPLE_BACKTRACE_H_ */
diff -urN linux-2.6.25.1/include/asm-mips/stacktrace.h
linux-t/include/asm-mips/stacktrace.h
--- linux-2.6.25.1/include/asm-mips/stacktrace.h 2008-05-01
14:45:25.000000000 -0700
+++ linux-t/include/asm-mips/stacktrace.h 2008-05-06 17:30:09.000000000
-0700
@@ -30,18 +30,49 @@
".set noat\n\t"
#ifdef CONFIG_64BIT
"1: dla $1, 1b\n\t"
- "sd $1, %0\n\t"
- "sd $29, %1\n\t"
- "sd $31, %2\n\t"
+ "sd $1, %[epc]\n\t"
+ "sd $16, %[s0]\n\t"
+ "sd $17, %[s1]\n\t"
+ "sd $18, %[s2]\n\t"
+ "sd $19, %[s3]\n\t"
+ "sd $20, %[s4]\n\t"
+ "sd $21, %[s5]\n\t"
+ "sd $22, %[s6]\n\t"
+ "sd $23, %[s7]\n\t"
+ "sd $28, %[gp]\n\t"
+ "sd $29, %[sp]\n\t"
+ "sd $30, %[fp]\n\t"
+ "sd $31, %[ra]\n\t"
#else
"1: la $1, 1b\n\t"
- "sw $1, %0\n\t"
- "sw $29, %1\n\t"
- "sw $31, %2\n\t"
+ "sw $1, %[epc]\n\t"
+ "sw $16, %[s0]\n\t"
+ "sw $17, %[s1]\n\t"
+ "sw $18, %[s2]\n\t"
+ "sw $19, %[s3]\n\t"
+ "sw $20, %[s4]\n\t"
+ "sw $21, %[s5]\n\t"
+ "sw $22, %[s6]\n\t"
+ "sw $23, %[s7]\n\t"
+ "sw $28, %[gp]\n\t"
+ "sw $29, %[sp]\n\t"
+ "sw $30, %[fp]\n\t"
+ "sw $31, %[ra]\n\t"
#endif
".set pop\n\t"
- : "=m" (regs->cp0_epc),
- "=m" (regs->regs[29]), "=m" (regs->regs[31])
+ : [epc] "=m" (regs->cp0_epc),
+ [s0] "=m" (regs->regs[16]),
+ [s1] "=m" (regs->regs[17]),
+ [s2] "=m" (regs->regs[18]),
+ [s3] "=m" (regs->regs[19]),
+ [s4] "=m" (regs->regs[20]),
+ [s5] "=m" (regs->regs[21]),
+ [s6] "=m" (regs->regs[22]),
+ [s7] "=m" (regs->regs[23]),
+ [gp] "=m" (regs->regs[28]),
+ [sp] "=m" (regs->regs[29]),
+ [fp] "=m" (regs->regs[30]),
+ [ra] "=m" (regs->regs[31])
: : "memory");
}
diff -urN linux-2.6.25.1/include/asm-mips/thread-backtrace.h
linux-t/include/asm-mips/thread-backtrace.h
--- linux-2.6.25.1/include/asm-mips/thread-backtrace.h 1969-12-31
16:00:00.000000000 -0800
+++ linux-t/include/asm-mips/thread-backtrace.h 2008-05-06
17:30:09.000000000 -0700
@@ -0,0 +1,229 @@
+/*
+ * thread-backtrace.h
+ *
+ * Backtrace code for Linux operating system processes. This means that, in
+ * addition to handling the normal ABI-conformant stack frames, it must
also
+ * handle Linux-specific signal stack frames.
+ *
+ * Copyright (C) 2007 Scientific-Atlanta, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+#ifndef _THREAD_BACKTRACE_H_
+#define _THREAD_BACKTRACE_H_
+#include <asm/simple-backtrace.h>
+#include <asm/ptrace.h>
+
+/* Value to be returned by the function that processes each frame to
indicate
+ * a non-error termination of the backtrace. */
+#define THREAD_BACKTRACE_END 1
+
+/* Value returned internally by functions called from thread_backtrace to
+ * indicate that the backtrace should terminate. */
+#define THREAD_BACKTRACE_DONE 2
+
+#define NUM_SIGARGS 4 /* # of signal function arguments */
+
+enum thread_frame_type {
+ THREAD_FRAME_SIMPLE, THREAD_FRAME_SIGNAL, THREAD_FRAME_RT_SIGNAL
+};
+
+/*
+ * This function must be supplied by the caller.
+ *
+ * Get a 64-bit object from memory. This is used by the register values
+ * in a sigframe structure.
+ * Params: rp Address of the value.
+ * reg Location in which to store the value.
+ * Returns: Zero on success, otherwise a negative errno value.
+ */
+typedef int (*get_sc_reg_t) (reg_t rp, reg_t *sc_reg);
+
+/*
+ * Configuration information
+ */
+struct thread_bt_config {
+ struct simple_bt_config simple_config;
+ get_sc_reg_t get_sc_reg;
+};
+
+/*
+ * Contains register information (s0-s8, sp, pc), the stack frame type and,
+ * if the stack frame type is THREAD_FRAME_SIGNAL or
THREAD_FRAME_RT_SIGNAL,
+ * the arguments to the signal handler.
+ */
+struct thread_bt {
+ struct simple_bt sbt;
+ const struct thread_bt_config *config;
+ enum thread_frame_type type;
+ reg_t sigargs [NUM_SIGARGS]; /* Arguments to */
+ /* the signal handler */
+ /* Protected */
+ int pc_is_return_address; /* If non-zero, the */
+ /* $pc value is the address saved */
+ /* by a jalr instruction, and thus */
+ /* should be decremented by two times */
+ /* OPCODE_SIZE to point to the jalr */
+};
+
+/*
+ * This is the function that thread_backtrace will call for each frame that
+ * it finds. It can terminate the backtrace by returning
THREAD_BACKTRACE_END,
+ * which be treated as a normal termination, or a negative errno value.
Negative
+ * errno values will be, in turn, returned by thread_backtrace to its
caller.
+ * then be returned by thread_backtrace.
+ * Params: arg Value passed to thread_backtrace.
+ * bt Pointer to the struct thread_bt object for the curent
+ * stack frame.
+ * Returns: Zero if the backtrace should continue, non-zero if the
backtrace
+ * should terminate. If the backtrace is terminating because an
+ * error occurred, it should return a negative errno value. If the
+ * backtrace is terminating because this function has reach the
+ * end of the backtrace, it should return the positive value
+ * THREAD_BACKTRACE_END.
+ */
+typedef int (*process_thread_frame_t) (void *arg, struct thread_bt *bt);
+
+/* Store the given register. Assumes that $at points to the location
+ * for the store. This is the 32-bit version. */
+#define STORE_REG(reg) "sw " #reg ",0($at)\n"
+
+/* Initialize the given struct thread_bt object that will be used in a call
+ * to thread_next_frame from the current function, or a function called
by the
+ * current function. This should be the first thing in the function so that
+ * the values of the registers are not altered from their values on
entry to
+ * the function. */
+#define thread_backtrace_init_here(bt, config) do { \
+ /* First, store $sp. Note that we use $at to do this */ \
+ __asm__ __volatile__ ( \
+ " .set noat\n" \
+ " la $at,%[sp]\n" /* SP */ \
+ STORE_REG($sp) \
+ : [sp] "=m" ((bt)->sbt.gprs [REG_SP]) \
+ : \
+ : "at" \
+ ); \
+ __asm__ __volatile__ ( \
+ " la $at,%[v0]\n" /* V0 */ \
+ STORE_REG($2) \
+ " la $at,%[v1]\n" /* V1 */ \
+ STORE_REG($3) \
+ \
+ " la $at,%[a0]\n" /* A0 */ \
+ STORE_REG($4) \
+ " la $at,%[a1]\n" /* A1 */ \
+ STORE_REG($5) \
+ " la $at,%[a2]\n" /* A2 */ \
+ STORE_REG($6) \
+ " la $at,%[a3]\n" /* A3 */ \
+ STORE_REG($7) \
+ \
+ " la $at,%[t0]\n" /* T0 */ \
+ STORE_REG($8) \
+ " la $at,%[t1]\n" /* T1 */ \
+ STORE_REG($9) \
+ " la $at,%[t2]\n" /* T2 */ \
+ STORE_REG($10) \
+ " la $at,%[t3]\n" /* T3 */ \
+ STORE_REG($11) \
+ " la $at,%[t4]\n" /* T4 */ \
+ STORE_REG($12) \
+ " la $at,%[t5]\n" /* T5 */ \
+ STORE_REG($13) \
+ " la $at,%[t6]\n" /* T6 */ \
+ STORE_REG($14) \
+ " la $at,%[t7]\n" /* T7 */ \
+ STORE_REG($15) \
+ \
+ " la $at,%[s0]\n" /* S0 */ \
+ STORE_REG($16) \
+ " la $at,%[s1]\n" /* S1 */ \
+ STORE_REG($17) \
+ " la $at,%[s2]\n" /* S2 */ \
+ STORE_REG($18) \
+ " la $at,%[s3]\n" /* S3 */ \
+ STORE_REG($19) \
+ " la $at,%[s4]\n" /* S4 */ \
+ STORE_REG($20) \
+ " la $at,%[s5]\n" /* S5 */ \
+ STORE_REG($21) \
+ " la $at,%[s6]\n" /* S6 */ \
+ STORE_REG($22) \
+ " la $at,%[s7]\n" /* S7 */ \
+ STORE_REG($23) \
+ \
+ " la $at,%[t8]\n" /* T8 */ \
+ STORE_REG($24) \
+ " la $at,%[t9]\n" /* T9 */ \
+ STORE_REG($25) \
+ \
+ " la $at,%[k0]\n" /* K0 */ \
+ STORE_REG($26) \
+ " la $at,%[k1]\n" /* K1 */ \
+ STORE_REG($27) \
+ \
+ " la $at,%[gp]\n" /* GP */ \
+ STORE_REG($gp) \
+ " la $at,%[s8]\n" /* FP/S8 */ \
+ STORE_REG($fp) \
+ " la $at,%[ra]\n" /* RA */ \
+ STORE_REG($ra) \
+ : [v0] "=m" ((bt)->sbt.gprs [REG_V0]), \
+ [v1] "=m" ((bt)->sbt.gprs [REG_V1]), \
+ [a0] "=m" ((bt)->sbt.gprs [REG_A0]), \
+ [a1] "=m" ((bt)->sbt.gprs [REG_A1]), \
+ [a2] "=m" ((bt)->sbt.gprs [REG_A2]), \
+ [a3] "=m" ((bt)->sbt.gprs [REG_A3]), \
+ [t0] "=m" ((bt)->sbt.gprs [REG_T0]), \
+ [t1] "=m" ((bt)->sbt.gprs [REG_T1]), \
+ [t2] "=m" ((bt)->sbt.gprs [REG_T2]), \
+ [t3] "=m" ((bt)->sbt.gprs [REG_T3]), \
+ [t4] "=m" ((bt)->sbt.gprs [REG_T4]), \
+ [t5] "=m" ((bt)->sbt.gprs [REG_T5]), \
+ [t6] "=m" ((bt)->sbt.gprs [REG_T6]), \
+ [t7] "=m" ((bt)->sbt.gprs [REG_T7]), \
+ [s0] "=m" ((bt)->sbt.gprs [REG_S0]), \
+ [s1] "=m" ((bt)->sbt.gprs [REG_S1]), \
+ [s2] "=m" ((bt)->sbt.gprs [REG_S2]), \
+ [s3] "=m" ((bt)->sbt.gprs [REG_S3]), \
+ [s4] "=m" ((bt)->sbt.gprs [REG_S4]), \
+ [s5] "=m" ((bt)->sbt.gprs [REG_S5]), \
+ [s6] "=m" ((bt)->sbt.gprs [REG_S6]), \
+ [s7] "=m" ((bt)->sbt.gprs [REG_S7]), \
+ [t8] "=m" ((bt)->sbt.gprs [REG_T8]), \
+ [t9] "=m" ((bt)->sbt.gprs [REG_T9]), \
+ [k0] "=m" ((bt)->sbt.gprs [REG_K0]), \
+ [k1] "=m" ((bt)->sbt.gprs [REG_K1]), \
+ [gp] "=m" ((bt)->sbt.gprs [REG_GP]), \
+ [s8] "=m" ((bt)->sbt.gprs [REG_S8]), \
+ [ra] "=m" ((bt)->sbt.gprs [REG_RA]) \
+ : \
+ : "at" \
+ ); \
+ thread_backtrace_init(bt, config); \
+ (bt)->sbt.pc = thread_backtrace_here(); \
+ } while (0)
+
+extern int thread_backtrace(process_thread_frame_t process, void *arg,
+ const struct thread_bt_config *config);
+extern void thread_backtrace_init(struct thread_bt *bt,
+ const struct thread_bt_config *config);
+extern reg_t thread_backtrace_here(void);
+extern int thread_backtrace_first(struct thread_bt *bt);
+extern int thread_backtrace_next(struct thread_bt *bt);
+extern int thread_backtrace_pop_frame(struct thread_bt *bt);
+extern int thread_backtrace_analyze_frame(struct thread_bt *bt);
+#endif /* _THREAD_BACKTRACE_H_ */
next reply other threads:[~2008-05-07 23:50 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-05-07 23:49 David VomLehn [this message]
2008-05-11 16:07 ` [RFC] [PATCH 1/1] [MIPS] Advanced Kernel Stack Backtrace Ralf Baechle
-- strict thread matches above, loose matches on Subject: below --
2008-05-12 17:14 David VomLehn
2008-05-12 17:14 ` David VomLehn
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=48224021.7050306@cisco.com \
--to=dvomlehn@cisco.com \
--cc=linux-mips@linux-mips.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.