Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b344e24a authored by Matt Fleming's avatar Matt Fleming
Browse files

sh: unwinder: Introduce UNWINDER_BUG() and UNWINDER_BUG_ON()



We can't assume that if we execute the unwinder code and the unwinder
was already running that it has faulted. Clearly two kernel threads can
invoke the unwinder at the same time and may be running simultaneously.

The previous approach used BUG() and BUG_ON() in the unwinder code to
detect whether the unwinder was incapable of unwinding the stack, and
that the next available unwinder should be used instead. A better
approach is to explicitly invoke a trap handler to switch unwinders when
the current unwinder cannot continue.

Signed-off-by: default avatarMatt Fleming <matt@console-pimps.org>
parent 97efbbd5
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
#ifndef __ASM_SH_BUG_H
#define __ASM_SH_BUG_H

#define TRAPA_UNWINDER_BUG_OPCODE 0xc33b /* trapa #0x3b */
#define TRAPA_BUG_OPCODE	0xc33e	/* trapa #0x3e */

#ifdef CONFIG_GENERIC_BUG
@@ -72,6 +73,30 @@ do { \
	unlikely(__ret_warn_on);				\
})

#define UNWINDER_BUG()						\
do {							\
	__asm__ __volatile__ (				\
		"1:\t.short %O0\n"			\
		_EMIT_BUG_ENTRY		\
		 :					\
		 : "n" (TRAPA_UNWINDER_BUG_OPCODE),	\
		   "i" (__FILE__),			\
		   "i" (__LINE__), "i" (0),		\
		   "i" (sizeof(struct bug_entry)));	\
} while (0)

#define UNWINDER_BUG_ON(x) ({					\
	int __ret_unwinder_on = !!(x);				\
	if (__builtin_constant_p(__ret_unwinder_on)) {		\
		if (__ret_unwinder_on)				\
			UNWINDER_BUG();				\
	} else {						\
		if (unlikely(__ret_unwinder_on))		\
			UNWINDER_BUG();				\
	}							\
	unlikely(__ret_unwinder_on);				\
})

#endif /* CONFIG_GENERIC_BUG */

#include <asm-generic/bug.h>
+5 −0
Original line number Diff line number Diff line
@@ -181,6 +181,11 @@ BUILD_TRAP_HANDLER(breakpoint);
BUILD_TRAP_HANDLER(singlestep);
BUILD_TRAP_HANDLER(fpu_error);
BUILD_TRAP_HANDLER(fpu_state_restore);
BUILD_TRAP_HANDLER(unwinder);

#ifdef CONFIG_BUG
extern void handle_BUG(struct pt_regs *);
#endif

#define arch_align_stack(x) (x)

+6 −0
Original line number Diff line number Diff line
@@ -22,4 +22,10 @@ extern void stack_reader_dump(struct task_struct *, struct pt_regs *,
			      unsigned long *, const struct stacktrace_ops *,
			      void *);

/*
 * Used by fault handling code to signal to the unwinder code that it
 * should switch to a different unwinder.
 */
extern int unwinder_faulted;

#endif /* _LINUX_UNWINDER_H */
+5 −1
Original line number Diff line number Diff line
@@ -19,6 +19,10 @@

#if !defined(CONFIG_SH_STANDARD_BIOS)
#define sh_bios_handler			debug_trap_handler
#endif

#if !defined(CONFIG_DWARF_UNWINDER)
#define unwinder_trap_handler		debug_trap_handler
#endif

	.data
@@ -35,7 +39,7 @@ ENTRY(debug_trap_table)
	.long debug_trap_handler	/* 0x38 */
	.long debug_trap_handler	/* 0x39 */
	.long debug_trap_handler	/* 0x3a */
	.long debug_trap_handler	/* 0x3b */
	.long unwinder_trap_handler	/* 0x3b */
	.long breakpoint_trap_handler	/* 0x3c */
	.long singlestep_trap_handler	/* 0x3d */
	.long bug_trap_handler		/* 0x3e */
+21 −17
Original line number Diff line number Diff line
@@ -69,7 +69,7 @@ static struct dwarf_reg *dwarf_frame_alloc_reg(struct dwarf_frame *frame,
		 * Let's just bomb hard here, we have no way to
		 * gracefully recover.
		 */
		BUG();
		UNWINDER_BUG();
	}

	reg->number = reg_num;
@@ -232,7 +232,7 @@ static int dwarf_read_encoded_value(char *addr, unsigned long *val,
		break;
	default:
		pr_debug("encoding=0x%x\n", (encoding & 0x70));
		BUG();
		UNWINDER_BUG();
	}

	if ((encoding & 0x07) == 0x00)
@@ -247,7 +247,7 @@ static int dwarf_read_encoded_value(char *addr, unsigned long *val,
		break;
	default:
		pr_debug("encoding=0x%x\n", encoding);
		BUG();
		UNWINDER_BUG();
	}

	return count;
@@ -519,6 +519,7 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start,
			break;
		default:
			pr_debug("unhandled DWARF instruction 0x%x\n", insn);
			UNWINDER_BUG();
			break;
		}
	}
@@ -558,7 +559,7 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc,
	frame = mempool_alloc(dwarf_frame_pool, GFP_ATOMIC);
	if (!frame) {
		printk(KERN_ERR "Unable to allocate a dwarf frame\n");
		BUG();
		UNWINDER_BUG();
	}

	INIT_LIST_HEAD(&frame->reg_list);
@@ -605,7 +606,8 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc,
	case DWARF_FRAME_CFA_REG_OFFSET:
		if (prev) {
			reg = dwarf_frame_reg(prev, frame->cfa_register);
			BUG_ON(!reg);
			UNWINDER_BUG_ON(!reg);
			UNWINDER_BUG_ON(reg->flags != DWARF_REG_OFFSET);

			addr = prev->cfa + reg->addr;
			frame->cfa = __raw_readl(addr);
@@ -624,12 +626,13 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc,
		frame->cfa += frame->cfa_offset;
		break;
	default:
		BUG();
		UNWINDER_BUG();
	}

	/* If we haven't seen the return address reg, we're screwed. */
	reg = dwarf_frame_reg(frame, DWARF_ARCH_RA_REG);
	BUG_ON(!reg);
	UNWINDER_BUG_ON(!reg);
	UNWINDER_BUG_ON(reg->flags != DWARF_REG_OFFSET);

	addr = frame->cfa + reg->addr;
	frame->return_addr = __raw_readl(addr);
@@ -664,7 +667,7 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len,
	cie->cie_pointer = (unsigned long)entry;

	cie->version = *(char *)p++;
	BUG_ON(cie->version != 1);
	UNWINDER_BUG_ON(cie->version != 1);

	cie->augmentation = p;
	p += strlen(cie->augmentation) + 1;
@@ -694,7 +697,7 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len,
		count = dwarf_read_uleb128(p, &length);
		p += count;

		BUG_ON((unsigned char *)p > end);
		UNWINDER_BUG_ON((unsigned char *)p > end);

		cie->initial_instructions = p + length;
		cie->augmentation++;
@@ -722,16 +725,16 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len,
			 * routine in the CIE
			 * augmentation.
			 */
			BUG();
			UNWINDER_BUG();
		} else if (*cie->augmentation == 'S') {
			BUG();
			UNWINDER_BUG();
		} else {
			/*
			 * Unknown augmentation. Assume
			 * 'z' augmentation.
			 */
			p = cie->initial_instructions;
			BUG_ON(!p);
			UNWINDER_BUG_ON(!p);
			break;
		}
	}
@@ -805,9 +808,11 @@ static int dwarf_parse_fde(void *entry, u32 entry_type,
	return 0;
}

static void dwarf_unwinder_dump(struct task_struct *task, struct pt_regs *regs,
static void dwarf_unwinder_dump(struct task_struct *task,
				struct pt_regs *regs,
				unsigned long *sp,
				const struct stacktrace_ops *ops, void *data)
				const struct stacktrace_ops *ops,
				void *data)
{
	struct dwarf_frame *frame, *_frame;
	unsigned long return_addr;
@@ -831,7 +836,6 @@ static void dwarf_unwinder_dump(struct task_struct *task, struct pt_regs *regs,
		return_addr = frame->return_addr;
		ops->address(data, return_addr, 1);
	}

}

static struct unwinder dwarf_unwinder = {
Loading