[Patch 1/1][MIPS] Advanced kernel stack backtrace v2

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Advanced MIPS stack backtrace code

Disadvantages:
- Larger than the default code
- Requires KALLSYMS, making the kernel even larger
- Only partial support for 64-bit systems and what there is is untested.

Advantages:
- Produces backtraces including nested exceptions and interrupts.
- Uses __get_user() to handle stack and instructions access, making it much
 less vulnerable to stack corruption issues.
- Smart enough to handle instructions in branch delay slots.
- Detects where a register is used as a stack frame pointer
- Works in cases where a function returns from the middle
- Has been in use for well over a year

Changes for v2:
- Changed to using ARRAY_SIZE.
- Passes with no checkpatch.pl errors. There is one warning, which comes from
 a printk with no priority. This replaces a printk that also had no priority
 and the case could be made that correct priority to use is the default
 priority.
- Additional work to support 64-bit systems has been done, and warnings added
 to code that still needs work.

Signed-off-by: David VomLehn <dvomlehn@xxxxxxxxx>
---
Index: linux-2.6.25.1/arch/mips/Kconfig.debug
===================================================================
--- linux-2.6.25.1.orig/arch/mips/Kconfig.debug
+++ linux-2.6.25.1/arch/mips/Kconfig.debug
@@ -73,6 +73,17 @@ config RUNTIME_DEBUG
	  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
Index: linux-2.6.25.1/arch/mips/kernel/Makefile
===================================================================
--- linux-2.6.25.1.orig/arch/mips/kernel/Makefile
+++ linux-2.6.25.1/arch/mips/kernel/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_I8253)		+= i8253.o

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)

Index: linux-2.6.25.1/arch/mips/kernel/backtrace/Makefile
===================================================================
--- /dev/null
+++ linux-2.6.25.1/arch/mips/kernel/backtrace/Makefile
@@ -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
Index: linux-2.6.25.1/arch/mips/kernel/backtrace/kernel-backtrace-symbols.c
===================================================================
--- /dev/null
+++ linux-2.6.25.1/arch/mips/kernel/backtrace/kernel-backtrace-symbols.c
@@ -0,0 +1,42 @@
+/*
+ *			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
+ */
+
+#include <linux/kernel.h>
+
+/* 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 mips_instruction	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 = ARRAY_SIZE(kernel_backtrace_symbols);
Index: linux-2.6.25.1/arch/mips/kernel/backtrace/kernel-backtrace.c
===================================================================
--- /dev/null
+++ linux-2.6.25.1/arch/mips/kernel/backtrace/kernel-backtrace.c
@@ -0,0 +1,976 @@
+/*
+ *				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 <linux/uaccess.h>
+#include <asm/system.h>
+#include <asm/mipsregs.h>
+#include <asm/asm-offsets.h>
+#include <asm/kernel-backtrace.h>
+#include <asm/kernel-backtrace-symbols.h>
+
+/* 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(unsigned long pc, unsigned start, unsigned size);
+static const struct kern_special_sym *special_symbol(unsigned long pc,
+	unsigned long *start, unsigned long *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, unsigned long ip,
+	unsigned long ptr);
+static int update_saved_register(struct kernel_bt *bt, unsigned long ptr,
+	int offset);
+static int read_pt_regs(struct kernel_bt *bt);
+static int get_op(unsigned long ip, mips_instruction *op);
+static int get_reg(unsigned long rp, unsigned long *reg);
+static int symbol_lookup(unsigned long ip, unsigned long *start,
+	unsigned long *size);
+static int get_sc_reg(unsigned long rp, unsigned long *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 */
+	unsigned long			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(unsigned long pc, unsigned start, unsigned size)
+{
+	int			result;
+
+	if (ebase == 0)
+		result = 0;
+
+	else {
+		unsigned long	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(unsigned long pc,
+	unsigned long *start, unsigned long *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;
+		mips_instruction	*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 = (mips_instruction *) (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 = (unsigned long) 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;
+	unsigned long		ip;
+	mips_instruction	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;
+	unsigned long	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, unsigned long ip,
+	unsigned long ptr)
+{
+	int		result = 0;	/* Assume success */
+	mips_instruction	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,
+	unsigned long 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, unsigned long ptr,
+	int offset)
+{
+	int	result = 0;		/* Assume success */
+	unsigned long	cp0_status;
+	unsigned long	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;
+	mips_instruction	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;
+	unsigned long		pt_regs;
+	enum reg_num	i;
+	unsigned long		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(unsigned long ip, unsigned long *start,
+	unsigned long *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(unsigned long ip, mips_instruction *op)
+{
+	int		result;
+
+	result = __get_user(*op, (mips_instruction *) 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(unsigned long rp, unsigned long *reg)
+{
+	int		result;
+
+	result = __get_user(*reg, (unsigned long *) 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(unsigned long rp, unsigned long *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;
+}
Index: linux-2.6.25.1/arch/mips/kernel/backtrace/simple-backtrace.c
===================================================================
--- /dev/null
+++ linux-2.6.25.1/arch/mips/kernel/backtrace/simple-backtrace.c
@@ -0,0 +1,1242 @@
+/*
+ *				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>
+
+/* 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, unsigned long ip);
+	int(*find_return) (struct simple_bt *bt);
+};
+
+static int read_reg_if_saved(struct simple_bt *bt, mips_instruction op,
+	unsigned long fp);
+static int read_saved_reg(struct simple_bt *bt, mips_instruction op,
+	unsigned long fp);
+static int read_saved_registers(struct simple_bt *bt, unsigned long 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,
+	mips_instruction op);
+static int analyze_return_block(struct simple_bt *bt);
+static void analyze_return_block_op(struct simple_bt *bt, unsigned long ip,
+	mips_instruction op);
+static void complete_analysis(struct simple_bt *bt);
+
+static int start_frame_abi(struct simple_bt *bt, unsigned long ip);
+static int back_up_from_sp_decrement(struct simple_bt *bt, unsigned long ip);
+static int find_return_abi(struct simple_bt *bt);
+
+static int start_frame_lookup(struct simple_bt *bt, unsigned long ip);
+static int find_return_lookup(struct simple_bt *bt);
+static int find_return_lookup_bounded(struct simple_bt *bt, unsigned long start,
+	unsigned long 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 mips_instruction-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, unsigned long ip,
+	mips_instruction *op)
+{
+	return bt->config->get_op(ip, op);
+}
+
+/*
+ * Get a register-sized(unsigned long-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, unsigned long rp,
+	unsigned long *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, unsigned long addr,
+	unsigned long *symbolstart, unsigned long *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(mips_instruction 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(mips_instruction 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(mips_instruction 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(mips_instruction 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(mips_instruction 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(mips_instruction 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(mips_instruction 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 < ARRAY_SIZE(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, unsigned long ip,
+	mips_instruction 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;
+	unsigned long		ip;
+	mips_instruction	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;
+	unsigned long		ip;
+	mips_instruction	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) {
+		mips_instruction	prev_op;
+		unsigned long		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, unsigned long ip,
+	mips_instruction 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 */
+	unsigned long		ip;
+	mips_instruction	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,
+	mips_instruction 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 */
+	unsigned long	new_sp;
+	unsigned long	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, unsigned long fp)
+{
+	int		result = 0;	/* No errors so far */
+	mips_instruction	op;
+	unsigned long		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, mips_instruction op,
+	unsigned long 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, mips_instruction op,
+	unsigned long fp)
+{
+	int		result = 0;		/* Assume success */
+	enum reg_num	rt;
+	int		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]) {
+			unsigned long	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, unsigned long addr)
+{
+	int		result;
+	unsigned long		ip;
+	mips_instruction	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(mips_instruction op1,
+	mips_instruction 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(mips_instruction op1,
+	mips_instruction op2, mips_instruction 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, unsigned long sp_dec)
+{
+	int		result = 0;
+	unsigned long		ip;
+	unsigned	i;
+	mips_instruction	op [3];
+
+	/* Start by trying to read the three previous instructions. */
+	for (i = 0, ip = ip_prev(sp_dec);
+		i < ARRAY_SIZE(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;
+	unsigned long		ip;
+	mips_instruction	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, unsigned long 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;
+	unsigned long		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, unsigned long start,
+	unsigned long end)
+{
+	int			result = 0;	/* Assume success */
+	unsigned long		ip;
+	mips_instruction	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;
+}
Index: linux-2.6.25.1/arch/mips/kernel/backtrace/thread-backtrace.c
===================================================================
--- /dev/null
+++ linux-2.6.25.1/arch/mips/kernel/backtrace/thread-backtrace.c
@@ -0,0 +1,384 @@
+/*
+ *			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>
+
+static int restore_sigcontext_regs(struct thread_bt *bt, unsigned long ctx);
+static int set_sigargs(struct thread_bt *bt, unsigned long sigframe_args,
+	unsigned long 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.
+ */
+unsigned long thread_backtrace_here(void)
+{
+	unsigned long ra;
+	__asm__ __volatile__(
+		"	.set	noat\n"
+			LOAD_ADDR(%[ra])
+			STORE_REG($ra)
+		:	[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;
+	mips_instruction	op1, op2;
+	unsigned long		ip;
+	unsigned long		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;
+	unsigned long	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, unsigned long ctx)
+{
+	int		result = 0;		/* Assume success */
+	unsigned long		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, unsigned long sigframe_args,
+	unsigned long sigargs [NUM_SIGARGS])
+{
+	int		result = 0;	/* Assume success */
+	unsigned	i;
+
+	for (i = 0; result == 0 && i < NUM_SIGARGS; i++) {
+		unsigned long	p;
+		p = sigframe_args + i * sizeof(unsigned long);
+		result = bt->config->simple_config.get_reg(p, &sigargs [i]);
+	}
+
+	return result;
+}
Index: linux-2.6.25.1/arch/mips/kernel/entry.S
===================================================================
--- linux-2.6.25.1.orig/arch/mips/kernel/entry.S
+++ linux-2.6.25.1/arch/mips/kernel/entry.S
@@ -40,6 +40,7 @@ FEXPORT(__ret_from_irq)
	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,11 +51,13 @@ resume_userspace:
	j	restore_all

#ifdef CONFIG_PREEMPT
+	.globl	resume_kernel
resume_kernel:
	local_irq_disable
	lw	t0, TI_PRE_COUNT($28)
	bnez	t0, restore_all
-need_resched:
+	.globl	_need_resched
+_need_resched:
	LONG_L	t0, TI_FLAGS($28)
	andi	t1, t0, _TIF_NEED_RESCHED
	beqz	t1, restore_all
@@ -62,7 +65,7 @@ need_resched:
	andi	t0, 1
	beqz	t0, restore_all
	jal	preempt_schedule_irq
-	b	need_resched
+	b	_need_resched
#endif

FEXPORT(ret_from_fork)
@@ -141,9 +144,11 @@ FEXPORT(restore_partial)		# restore part
	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 @@ work_resched:
	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 @@ work_notifysig:				# deal with pending s

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
Index: linux-2.6.25.1/arch/mips/kernel/scall32-o32.S
===================================================================
--- linux-2.6.25.1.orig/arch/mips/kernel/scall32-o32.S
+++ linux-2.6.25.1/arch/mips/kernel/scall32-o32.S
@@ -54,6 +54,7 @@ NESTED(handle_sys, PT_SIZE, sp)
	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 @@ stack_done:
					# 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 @@ o32_syscall_exit:

	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 @@ syscall_trace_entry:
	 * 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 @@ stackargs:
	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 @@ stackargs:
	.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 @@ bad_stack:
	/*
	 * 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 @@ illegal_syscall:
#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 @@ illegal_syscall:

	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 @@ bad_alignment:
	jr	t2
	/* Unreached */

+	.globl	einval
einval:	li	v0, -EINVAL
	jr	ra
	END(sys_syscall)
Index: linux-2.6.25.1/arch/mips/kernel/signal.c
===================================================================
--- linux-2.6.25.1.orig/arch/mips/kernel/signal.c
+++ linux-2.6.25.1/arch/mips/kernel/signal.c
@@ -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)
Index: linux-2.6.25.1/arch/mips/kernel/signal32.c
===================================================================
--- linux-2.6.25.1.orig/arch/mips/kernel/signal32.c
+++ linux-2.6.25.1/arch/mips/kernel/signal32.c
@@ -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;
Index: linux-2.6.25.1/arch/mips/kernel/traps.c
===================================================================
--- linux-2.6.25.1.orig/arch/mips/kernel/traps.c
+++ linux-2.6.25.1/arch/mips/kernel/traps.c
@@ -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 @@ static int __init set_raw_show_trace(cha
__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");
}

Index: linux-2.6.25.1/include/asm-mips/kernel-backtrace-symbols.h
===================================================================
--- /dev/null
+++ linux-2.6.25.1/include/asm-mips/kernel-backtrace-symbols.h
@@ -0,0 +1,155 @@
+/*
+ *			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 mips_instruction	*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 */
+#ifdef	CONFIG_32BIT
+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)
+#endif
+
+/* arch/mips/kernel/scall64-64.S */
+#ifdef	CONFIG_64BIT
+#warning TODO: include symbols for arch/mips/kernel/scall64-64.S
+#endif
+
+/* arch/mips/kernel/scall64-n32.S */
+#ifdef	CONFIG_MIPS32_N32
+#warning TODO: include symbols for arch/mips/kernel/scall64-n32.S
+#endif
+
+/* arch/mips/kernel/scall64-o32.S */
+#ifdef	CONFIG_MIPS32_O32
+#warning TODO: include symbols for arch/mips/kernel/scall64-o32.S
+#endif
+
+/* arch/mips/kernel/smtc-asm.S */
+#ifdef	CONFIG_MIPS32_MT_SMTC
+#warning TODO: include symbols for arch/mips/kernel/smtc-asm.S
+#endif
+
+/* 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 */
Index: linux-2.6.25.1/include/asm-mips/kernel-backtrace.h
===================================================================
--- /dev/null
+++ linux-2.6.25.1/include/asm-mips/kernel-backtrace.h
@@ -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 <linux/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 */
+	unsigned long		cp0_epc; /* CP0 EPC register */
+	unsigned long		start;	/* Start of current section of code */
+	unsigned long		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_ */
Index: linux-2.6.25.1/include/asm-mips/ptrace.h
===================================================================
--- linux-2.6.25.1.orig/include/asm-mips/ptrace.h
+++ linux-2.6.25.1/include/asm-mips/ptrace.h
@@ -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 @@ struct pt_regs {
#endif

	/* Saved main processor registers. */
-	unsigned long regs[32];
+	unsigned long regs[REG_ALL];

	/* Saved special registers. */
	unsigned long cp0_status;
Index: linux-2.6.25.1/include/asm-mips/sigframe.h
===================================================================
--- /dev/null
+++ linux-2.6.25.1/include/asm-mips/sigframe.h
@@ -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
Index: linux-2.6.25.1/include/asm-mips/simple-backtrace.h
===================================================================
--- /dev/null
+++ linux-2.6.25.1/include/asm-mips/simple-backtrace.h
@@ -0,0 +1,331 @@
+/*
+ *			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 <linux/ptrace.h>
+#include <asm/inst.h>
+
+/* Debugging code. This allows the backtrace code to be tested both within and
+ * outside of the kernel. */
+#ifdef	__KERNEL__
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#define	dbg_print	printk
+#define	PRIORITY	KERN_CRIT
+#endif
+
+#define	BACKTRACE_DEBUG	0
+
+#if	BACKTRACE_DEBUG != 0
+#define	bt_dbg(level, fmt, ...)	do {					\
+		if (level <= BACKTRACE_DEBUG)				\
+			dbg_print(PRIORITY "%s: " fmt, __func__,	\
+			## __VA_ARGS__);				\
+	} while (0)
+#else
+#define	bt_dbg(level, fmt, ...)	do { } while (0)
+#endif
+
+/* The MIPS processor has opcodes that are all the same length--4 bytes */
+#define	OPCODE_SIZE (sizeof(mips_instruction))
+
+/* 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 register type */
+#define	NULL_REG	((unsigned long) NULL)
+
+/* 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 */
+};
+
+/*
+ * Configuration information for the backtrace. This includes functions that
+ * are required by the simple-backtrace.c code but which must be supplied by
+ * users of that code:
+ * get_op - Get an mips_instruction-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.
+ *
+ * get_reg - Get a register-sized(unsigned long-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.
+ *
+ * symbol_lookup - 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.
+ */
+struct simple_bt_config {
+	enum simple_bt_type	type;
+	int			(*get_op)(unsigned long ip,
+					mips_instruction *op);
+	int			(*get_reg)(unsigned long rp,
+					unsigned long *reg);
+	int			(*symbol_lookup)(unsigned long addr,
+					unsigned long *symbolstart,
+					unsigned long *symbolsize);
+};
+
+/*
+ * Data type that holds the current values of the GPRS, along with other
+ * information required during the backtrace.
+ */
+struct simple_bt {
+	/* Public */
+	unsigned long	gprs [REG_ALL];
+	int		gpr_saved [REG_ALL];
+	enum reg_num	framepointer;	/* Register # of frame ptr */
+	unsigned long	pc;
+	int		frame_size;	/* Stack frame size, in bytes */
+	/* Private */
+	const struct 	simple_bt_config *config;
+	unsigned long	sf_allocation;	/* Stack frame allocation address */
+	unsigned long	fp_restore;	/* Addr frame ptr moved back to $sp*/
+	unsigned long	sf_deallocation; /* Addr of stack frame deallocation */
+	unsigned long	fn_return;	/* Address of return instruction */
+	enum reg_num	possible_framepointer;
+	unsigned long	func_start;	/* Address of 1st opcode of function */
+	union {
+		struct {
+			unsigned long	func_size;
+		} lookup;
+	} data;
+};
+
+/* Opcode: addiu rs, rt, imm */
+static inline int is_addiu(mips_instruction inst, unsigned long rs,
+	unsigned long 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(mips_instruction 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(mips_instruction inst, unsigned long rd,
+	unsigned long rs, unsigned long 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(mips_instruction 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(mips_instruction inst, unsigned long rd,
+	unsigned long rs)
+{
+	return is_addu(inst, rd, rs, REG_ZERO);
+}
+
+/* Opcode: jal addr */
+static inline int is_jal(mips_instruction inst)
+{
+	struct j_format	*op_p = (struct j_format *) &inst;
+	return op_p->opcode == jal_op;
+}
+
+static inline int is_jalr(mips_instruction 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(mips_instruction inst, unsigned long 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(mips_instruction inst, unsigned long rt)
+{
+	return is_addiu(inst, REG_ZERO, rt);
+}
+
+static inline int is_lui(mips_instruction inst, unsigned long 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(mips_instruction inst, unsigned long rt,
+	unsigned long 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(mips_instruction 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 int sign_extend_imm(mips_instruction imm)
+{
+	struct i_format	*op_p = (struct i_format *) &imm;
+	return op_p->simmediate;
+}
+
+static inline int sign_extend_offset(mips_instruction 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(mips_instruction 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(mips_instruction 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 int frame_save_offset(mips_instruction 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 mips_instruction *.
+ */
+static inline unsigned long ip_add(unsigned long ip, long 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 unsigned long ip_next(unsigned long 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 unsigned long ip_prev(unsigned long ip)
+{
+	return ip_add(ip, -OPCODE_SIZE);
+}
+
+extern int is_basic_block_end(mips_instruction 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_ */
Index: linux-2.6.25.1/include/asm-mips/stacktrace.h
===================================================================
--- linux-2.6.25.1.orig/include/asm-mips/stacktrace.h
+++ linux-2.6.25.1/include/asm-mips/stacktrace.h
@@ -30,18 +30,49 @@ static __always_inline void prepare_fram
		".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");
}

Index: linux-2.6.25.1/include/asm-mips/thread-backtrace.h
===================================================================
--- /dev/null
+++ linux-2.6.25.1/include/asm-mips/thread-backtrace.h
@@ -0,0 +1,242 @@
+/*
+ *				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 <linux/ptrace.h>
+#include <asm/simple-backtrace.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) (unsigned long rp, unsigned long *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;
+	unsigned long		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);
+
+/*
+ * Macros for lo0ading addresses and storing registers:
+ * LOAD_ADDR	Load the address of the given object into the $at register
+ * STORE_REG	Store the given register. Assumes that $at points to the
+ *		location for the store.
+ */
+#ifdef CONFIG_64BIT
+#warning TODO: 64-bit code needs to be verified
+#define	LOAD_ADDR(obj)	"dla	$at," #obj "\n"
+#define	STORE_REG(reg)	"sd	" #reg ",0($at)\n"
+#endif
+
+#ifdef CONFIG_32BIT
+#define	LOAD_ADDR(obj)	"la	$at," #obj "\n"
+#define	STORE_REG(reg)	"sw	" #reg ",0($at)\n"
+#endif
+
+/* 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"				\
+				LOAD_ADDR(%[sp])	/* SP */	\
+				STORE_REG($sp)				\
+		:	[sp] "=m" ((bt)->sbt.gprs [REG_SP])		\
+		:							\
+		:	"at"						\
+		);							\
+		__asm__ __volatile__ (					\
+				LOAD_ADDR(%[v0])	/* V0 */	\
+				STORE_REG($2)				\
+				LOAD_ADDR(%[v1])	/* V1 */	\
+				STORE_REG($3)				\
+									\
+				LOAD_ADDR(%[a0])	/* A0 */	\
+				STORE_REG($4)				\
+				LOAD_ADDR(%[a1])	/* A1 */	\
+				STORE_REG($5)				\
+				LOAD_ADDR(%[a2])	/* A2 */	\
+				STORE_REG($6)				\
+				LOAD_ADDR(%[a3])	/* A3 */	\
+				STORE_REG($7)				\
+									\
+				LOAD_ADDR(%[t0])	/* T0 */	\
+				STORE_REG($8)				\
+				LOAD_ADDR(%[t1])	/* T1 */	\
+				STORE_REG($9)				\
+				LOAD_ADDR(%[t2])	/* T2 */	\
+				STORE_REG($10)				\
+				LOAD_ADDR(%[t3])	/* T3 */	\
+				STORE_REG($11)				\
+				LOAD_ADDR(%[t4])	/* T4 */	\
+				STORE_REG($12)				\
+				LOAD_ADDR(%[t5])	/* T5 */	\
+				STORE_REG($13)				\
+				LOAD_ADDR(%[t6])	/* T6 */	\
+				STORE_REG($14)				\
+				LOAD_ADDR(%[t7])	/* T7 */	\
+				STORE_REG($15)				\
+									\
+				LOAD_ADDR(%[s0])	/* S0 */	\
+				STORE_REG($16)				\
+				LOAD_ADDR(%[s1])	/* S1 */	\
+				STORE_REG($17)				\
+				LOAD_ADDR(%[s2])	/* S2 */	\
+				STORE_REG($18)				\
+				LOAD_ADDR(%[s3])	/* S3 */	\
+				STORE_REG($19)				\
+				LOAD_ADDR(%[s4])	/* S4 */	\
+				STORE_REG($20)				\
+				LOAD_ADDR(%[s5])	/* S5 */	\
+				STORE_REG($21)				\
+				LOAD_ADDR(%[s6])	/* S6 */	\
+				STORE_REG($22)				\
+				LOAD_ADDR(%[s7])	/* S7 */	\
+				STORE_REG($23)				\
+									\
+				LOAD_ADDR(%[t8])	/* T8 */	\
+				STORE_REG($24)				\
+				LOAD_ADDR(%[t9])	/* T9 */	\
+				STORE_REG($25)				\
+									\
+				LOAD_ADDR(%[k0])	/* K0 */	\
+				STORE_REG($26)				\
+				LOAD_ADDR(%[k1])	/* K1 */	\
+				STORE_REG($27)				\
+									\
+				LOAD_ADDR(%[gp])	/* GP */	\
+				STORE_REG($gp)				\
+				LOAD_ADDR(%[s8])	/* FP/S8 */	\
+				STORE_REG($fp)				\
+				LOAD_ADDR(%[ra])	/* 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 unsigned long 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_ */



[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux